From eb7ab77e8c3d1b22a06841516a1efba03896c3e3 Mon Sep 17 00:00:00 2001 From: Novrus Shehaj <78491450+NovrusShehaj@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:06:16 -0400 Subject: [PATCH] Auth0 Integration Added for Sign up/Log In --- backend/.env.example | 4 + backend/biosim_server/common/auth/auth0.py | 75 + backend/biosim_server/config.py | 11 +- backend/biosim_server/dependencies.py | 3 +- backend/biosim_server/simulations/router.py | 15 +- backend/biosim_server/worker/worker_main.py | 4 +- backend/node_modules/.bin/nanoid | 1 + backend/node_modules/.bin/parser | 1 + backend/node_modules/.package-lock.json | 374 + .../node_modules/@auth0/auth0-auth-js/LICENSE | 21 + .../@auth0/auth0-auth-js/README.md | 364 + .../@auth0/auth0-auth-js/package.json | 61 + .../node_modules/@auth0/auth0-spa-js/LICENSE | 21 + .../@auth0/auth0-spa-js/README.md | 189 + .../@auth0/auth0-spa-js/package.json | 129 + .../@auth0/auth0-spa-js/src/Auth0Client.ts | 2273 ++ .../auth0-spa-js/src/Auth0Client.utils.ts | 226 + .../@auth0/auth0-spa-js/src/TokenExchange.ts | 106 + .../@auth0/auth0-spa-js/src/api.ts | 187 + .../src/cache/cache-localstorage.ts | 31 + .../auth0-spa-js/src/cache/cache-manager.ts | 389 + .../auth0-spa-js/src/cache/cache-memory.ts | 31 + .../@auth0/auth0-spa-js/src/cache/index.ts | 4 + .../auth0-spa-js/src/cache/key-manifest.ts | 53 + .../@auth0/auth0-spa-js/src/cache/shared.ts | 105 + .../@auth0/auth0-spa-js/src/constants.ts | 85 + .../@auth0/auth0-spa-js/src/dpop/dpop.ts | 56 + .../@auth0/auth0-spa-js/src/dpop/storage.ts | 136 + .../@auth0/auth0-spa-js/src/dpop/utils.ts | 70 + .../@auth0/auth0-spa-js/src/errors.ts | 181 + .../@auth0/auth0-spa-js/src/fetcher.ts | 271 + .../@auth0/auth0-spa-js/src/global.ts | 988 + .../@auth0/auth0-spa-js/src/http.ts | 250 + .../@auth0/auth0-spa-js/src/index.ts | 172 + .../@auth0/auth0-spa-js/src/jwt.ts | 225 + .../@auth0/auth0-spa-js/src/lock.ts | 141 + .../auth0-spa-js/src/mfa/MfaApiClient.ts | 422 + .../auth0-spa-js/src/mfa/MfaContextManager.ts | 128 + .../@auth0/auth0-spa-js/src/mfa/constants.ts | 48 + .../@auth0/auth0-spa-js/src/mfa/errors.ts | 154 + .../@auth0/auth0-spa-js/src/mfa/index.ts | 24 + .../@auth0/auth0-spa-js/src/mfa/types.ts | 209 + .../@auth0/auth0-spa-js/src/mfa/utils.ts | 41 + .../src/myaccount/MyAccountApiClient.ts | 283 + .../auth0-spa-js/src/myaccount/index.ts | 46 + .../auth0-spa-js/src/myaccount/types.ts | 355 + .../src/passkey/PasskeyApiClient.ts | 332 + .../@auth0/auth0-spa-js/src/passkey/errors.ts | 25 + .../@auth0/auth0-spa-js/src/passkey/index.ts | 22 + .../@auth0/auth0-spa-js/src/passkey/types.ts | 74 + .../@auth0/auth0-spa-js/src/promise-utils.ts | 29 + .../@auth0/auth0-spa-js/src/scope.ts | 74 + .../@auth0/auth0-spa-js/src/storage.ts | 145 + .../auth0-spa-js/src/transaction-manager.ts | 56 + .../@auth0/auth0-spa-js/src/utils.ts | 316 + .../@auth0/auth0-spa-js/src/version.ts | 1 + .../src/worker/__mocks__/token.worker.ts | 10 + .../auth0-spa-js/src/worker/token.worker.ts | 376 + .../auth0-spa-js/src/worker/worker.types.ts | 45 + .../auth0-spa-js/src/worker/worker.utils.ts | 38 + backend/node_modules/@auth0/auth0-vue/LICENSE | 1355 + .../node_modules/@auth0/auth0-vue/README.md | 181 + .../@auth0/auth0-vue/package.json | 139 + .../@auth0/auth0-vue/src/global.ts | 74 + .../@auth0/auth0-vue/src/guard.ts | 95 + .../@auth0/auth0-vue/src/index.cjs.ts | 3 + .../@auth0/auth0-vue/src/index.ts | 42 + .../auth0-vue/src/interfaces/app-state.ts | 13 + .../src/interfaces/auth0-plugin-options.ts | 30 + .../interfaces/auth0-vue-client-options.ts | 16 + .../src/interfaces/auth0-vue-client.ts | 410 + .../@auth0/auth0-vue/src/interfaces/index.ts | 4 + .../@auth0/auth0-vue/src/plugin.ts | 301 + .../@auth0/auth0-vue/src/token.ts | 17 + .../@auth0/auth0-vue/src/utils.ts | 55 + .../@auth0/auth0-vue/src/version.ts | 1 + .../@babel/helper-string-parser/LICENSE | 22 + .../@babel/helper-string-parser/README.md | 19 + .../@babel/helper-string-parser/package.json | 31 + .../helper-validator-identifier/LICENSE | 22 + .../helper-validator-identifier/README.md | 19 + .../helper-validator-identifier/package.json | 31 + .../node_modules/@babel/parser/CHANGELOG.md | 1073 + backend/node_modules/@babel/parser/LICENSE | 19 + backend/node_modules/@babel/parser/README.md | 19 + .../@babel/parser/bin/babel-parser.js | 15 + .../node_modules/@babel/parser/package.json | 50 + .../@babel/parser/typings/babel-parser.d.ts | 262 + backend/node_modules/@babel/types/LICENSE | 22 + backend/node_modules/@babel/types/README.md | 19 + .../node_modules/@babel/types/package.json | 39 + .../@jridgewell/sourcemap-codec/LICENSE | 19 + .../@jridgewell/sourcemap-codec/README.md | 264 + .../@jridgewell/sourcemap-codec/package.json | 63 + .../@jridgewell/sourcemap-codec/src/scopes.ts | 345 + .../sourcemap-codec/src/sourcemap-codec.ts | 111 + .../sourcemap-codec/src/strings.ts | 65 + .../@jridgewell/sourcemap-codec/src/vlq.ts | 55 + .../sourcemap-codec/types/scopes.d.cts | 50 + .../sourcemap-codec/types/scopes.d.cts.map | 1 + .../sourcemap-codec/types/scopes.d.mts | 50 + .../sourcemap-codec/types/scopes.d.mts.map | 1 + .../types/sourcemap-codec.d.cts | 9 + .../types/sourcemap-codec.d.cts.map | 1 + .../types/sourcemap-codec.d.mts | 9 + .../types/sourcemap-codec.d.mts.map | 1 + .../sourcemap-codec/types/strings.d.cts | 16 + .../sourcemap-codec/types/strings.d.cts.map | 1 + .../sourcemap-codec/types/strings.d.mts | 16 + .../sourcemap-codec/types/strings.d.mts.map | 1 + .../sourcemap-codec/types/vlq.d.cts | 7 + .../sourcemap-codec/types/vlq.d.cts.map | 1 + .../sourcemap-codec/types/vlq.d.mts | 7 + .../sourcemap-codec/types/vlq.d.mts.map | 1 + .../node_modules/@vue/compiler-core/LICENSE | 21 + .../node_modules/@vue/compiler-core/README.md | 1 + .../node_modules/@vue/compiler-core/index.js | 7 + .../@vue/compiler-core/package.json | 58 + .../node_modules/@vue/compiler-dom/LICENSE | 21 + .../node_modules/@vue/compiler-dom/README.md | 1 + .../node_modules/@vue/compiler-dom/index.js | 7 + .../@vue/compiler-dom/package.json | 57 + .../node_modules/@vue/compiler-sfc/LICENSE | 21 + .../node_modules/@vue/compiler-sfc/README.md | 79 + .../@vue/compiler-sfc/package.json | 67 + .../node_modules/@vue/compiler-ssr/LICENSE | 21 + .../node_modules/@vue/compiler-ssr/README.md | 1 + .../@vue/compiler-ssr/package.json | 34 + backend/node_modules/@vue/reactivity/LICENSE | 21 + .../node_modules/@vue/reactivity/README.md | 19 + backend/node_modules/@vue/reactivity/index.js | 7 + .../node_modules/@vue/reactivity/package.json | 55 + .../node_modules/@vue/runtime-core/LICENSE | 21 + .../node_modules/@vue/runtime-core/README.md | 28 + .../node_modules/@vue/runtime-core/index.js | 7 + .../@vue/runtime-core/package.json | 52 + backend/node_modules/@vue/runtime-dom/LICENSE | 21 + .../node_modules/@vue/runtime-dom/README.md | 13 + .../node_modules/@vue/runtime-dom/index.js | 7 + .../@vue/runtime-dom/package.json | 60 + .../node_modules/@vue/server-renderer/LICENSE | 21 + .../@vue/server-renderer/README.md | 178 + .../@vue/server-renderer/index.js | 7 + .../@vue/server-renderer/package.json | 53 + backend/node_modules/@vue/shared/LICENSE | 21 + backend/node_modules/@vue/shared/README.md | 3 + backend/node_modules/@vue/shared/index.js | 7 + backend/node_modules/@vue/shared/package.json | 47 + .../browser-tabs-lock/CHANGELOG.md | 25 + .../node_modules/browser-tabs-lock/LICENSE | 20 + .../node_modules/browser-tabs-lock/README.md | 157 + .../browser-tabs-lock/bundleEntry.d.ts | 3 + .../browser-tabs-lock/bundleEntry.js | 7 + .../node_modules/browser-tabs-lock/index.d.ts | 61 + .../node_modules/browser-tabs-lock/index.js | 400 + .../browser-tabs-lock/package.json | 52 + .../browser-tabs-lock/processLock.d.ts | 11 + .../browser-tabs-lock/processLock.js | 62 + .../browser-tabs-lock/scripts/postinstall.js | 5 + backend/node_modules/csstype/LICENSE | 19 + backend/node_modules/csstype/README.md | 291 + backend/node_modules/csstype/index.d.ts | 22569 ++++++++++++++++ backend/node_modules/csstype/index.js.flow | 6863 +++++ backend/node_modules/csstype/package.json | 70 + backend/node_modules/dpop/LICENSE.md | 21 + backend/node_modules/dpop/README.md | 78 + backend/node_modules/dpop/package.json | 48 + backend/node_modules/entities/LICENSE | 11 + backend/node_modules/entities/decode.d.ts | 3 + backend/node_modules/entities/decode.js | 3 + backend/node_modules/entities/escape.d.ts | 3 + backend/node_modules/entities/escape.js | 3 + backend/node_modules/entities/package.json | 120 + backend/node_modules/entities/readme.md | 143 + .../entities/src/decode-codepoint.ts | 81 + backend/node_modules/entities/src/decode.ts | 685 + backend/node_modules/entities/src/encode.ts | 93 + backend/node_modules/entities/src/escape.ts | 161 + .../entities/src/generated/.eslintrc.json | 10 + .../src/generated/decode-data-html.ts | 6 + .../entities/src/generated/decode-data-xml.ts | 6 + .../entities/src/generated/encode-html.ts | 17 + backend/node_modules/entities/src/index.ts | 187 + .../entities/src/internal/bin-trie-flags.ts | 16 + .../entities/src/internal/decode-shared.ts | 30 + .../entities/src/internal/encode-shared.ts | 121 + backend/node_modules/es-cookie/CHANGELOG.md | 50 + backend/node_modules/es-cookie/LICENSE.md | 20 + backend/node_modules/es-cookie/README.md | 217 + backend/node_modules/es-cookie/package.json | 46 + .../es-cookie/src/CookieAttributes.d.ts | 44 + .../es-cookie/src/CookieAttributes.js | 2 + .../node_modules/es-cookie/src/es-cookie.d.ts | 12 + .../node_modules/es-cookie/src/es-cookie.js | 82 + .../node_modules/estree-walker/CHANGELOG.md | 92 + backend/node_modules/estree-walker/LICENSE | 7 + backend/node_modules/estree-walker/README.md | 48 + .../node_modules/estree-walker/package.json | 37 + .../node_modules/estree-walker/src/async.js | 118 + .../node_modules/estree-walker/src/index.js | 35 + .../estree-walker/src/package.json | 1 + .../node_modules/estree-walker/src/sync.js | 118 + .../node_modules/estree-walker/src/walker.js | 61 + .../estree-walker/types/async.d.ts | 53 + .../estree-walker/types/index.d.ts | 56 + .../estree-walker/types/sync.d.ts | 53 + .../estree-walker/types/tsconfig.tsbuildinfo | 345 + .../estree-walker/types/walker.d.ts | 37 + backend/node_modules/jose/LICENSE.md | 21 + backend/node_modules/jose/README.md | 150 + backend/node_modules/jose/package.json | 198 + backend/node_modules/lodash/LICENSE | 47 + backend/node_modules/lodash/README.md | 39 + backend/node_modules/lodash/_DataView.js | 7 + backend/node_modules/lodash/_Hash.js | 32 + backend/node_modules/lodash/_LazyWrapper.js | 28 + backend/node_modules/lodash/_ListCache.js | 32 + backend/node_modules/lodash/_LodashWrapper.js | 22 + backend/node_modules/lodash/_Map.js | 7 + backend/node_modules/lodash/_MapCache.js | 32 + backend/node_modules/lodash/_Promise.js | 7 + backend/node_modules/lodash/_Set.js | 7 + backend/node_modules/lodash/_SetCache.js | 27 + backend/node_modules/lodash/_Stack.js | 27 + backend/node_modules/lodash/_Symbol.js | 6 + backend/node_modules/lodash/_Uint8Array.js | 6 + backend/node_modules/lodash/_WeakMap.js | 7 + backend/node_modules/lodash/_apply.js | 21 + .../node_modules/lodash/_arrayAggregator.js | 22 + backend/node_modules/lodash/_arrayEach.js | 22 + .../node_modules/lodash/_arrayEachRight.js | 21 + backend/node_modules/lodash/_arrayEvery.js | 23 + backend/node_modules/lodash/_arrayFilter.js | 25 + backend/node_modules/lodash/_arrayIncludes.js | 17 + .../node_modules/lodash/_arrayIncludesWith.js | 22 + backend/node_modules/lodash/_arrayLikeKeys.js | 49 + backend/node_modules/lodash/_arrayMap.js | 21 + backend/node_modules/lodash/_arrayPush.js | 20 + backend/node_modules/lodash/_arrayReduce.js | 26 + .../node_modules/lodash/_arrayReduceRight.js | 24 + backend/node_modules/lodash/_arraySample.js | 15 + .../node_modules/lodash/_arraySampleSize.js | 17 + backend/node_modules/lodash/_arrayShuffle.js | 15 + backend/node_modules/lodash/_arraySome.js | 23 + backend/node_modules/lodash/_asciiSize.js | 12 + backend/node_modules/lodash/_asciiToArray.js | 12 + backend/node_modules/lodash/_asciiWords.js | 15 + .../node_modules/lodash/_assignMergeValue.js | 20 + backend/node_modules/lodash/_assignValue.js | 28 + backend/node_modules/lodash/_assocIndexOf.js | 21 + .../node_modules/lodash/_baseAggregator.js | 21 + backend/node_modules/lodash/_baseAssign.js | 17 + backend/node_modules/lodash/_baseAssignIn.js | 17 + .../node_modules/lodash/_baseAssignValue.js | 25 + backend/node_modules/lodash/_baseAt.js | 23 + backend/node_modules/lodash/_baseClamp.js | 22 + backend/node_modules/lodash/_baseClone.js | 166 + backend/node_modules/lodash/_baseConforms.js | 18 + .../node_modules/lodash/_baseConformsTo.js | 27 + backend/node_modules/lodash/_baseCreate.js | 30 + backend/node_modules/lodash/_baseDelay.js | 21 + .../node_modules/lodash/_baseDifference.js | 67 + backend/node_modules/lodash/_baseEach.js | 14 + backend/node_modules/lodash/_baseEachRight.js | 14 + backend/node_modules/lodash/_baseEvery.js | 21 + backend/node_modules/lodash/_baseExtremum.js | 32 + backend/node_modules/lodash/_baseFill.js | 32 + backend/node_modules/lodash/_baseFilter.js | 21 + backend/node_modules/lodash/_baseFindIndex.js | 24 + backend/node_modules/lodash/_baseFindKey.js | 23 + backend/node_modules/lodash/_baseFlatten.js | 38 + backend/node_modules/lodash/_baseFor.js | 16 + backend/node_modules/lodash/_baseForOwn.js | 16 + .../node_modules/lodash/_baseForOwnRight.js | 16 + backend/node_modules/lodash/_baseForRight.js | 15 + backend/node_modules/lodash/_baseFunctions.js | 19 + backend/node_modules/lodash/_baseGet.js | 24 + .../node_modules/lodash/_baseGetAllKeys.js | 20 + backend/node_modules/lodash/_baseGetTag.js | 28 + backend/node_modules/lodash/_baseGt.js | 14 + backend/node_modules/lodash/_baseHas.js | 19 + backend/node_modules/lodash/_baseHasIn.js | 13 + backend/node_modules/lodash/_baseInRange.js | 18 + backend/node_modules/lodash/_baseIndexOf.js | 20 + .../node_modules/lodash/_baseIndexOfWith.js | 23 + .../node_modules/lodash/_baseIntersection.js | 74 + backend/node_modules/lodash/_baseInverter.js | 21 + backend/node_modules/lodash/_baseInvoke.js | 24 + .../node_modules/lodash/_baseIsArguments.js | 18 + .../node_modules/lodash/_baseIsArrayBuffer.js | 17 + backend/node_modules/lodash/_baseIsDate.js | 18 + backend/node_modules/lodash/_baseIsEqual.js | 28 + .../node_modules/lodash/_baseIsEqualDeep.js | 83 + backend/node_modules/lodash/_baseIsMap.js | 18 + backend/node_modules/lodash/_baseIsMatch.js | 62 + backend/node_modules/lodash/_baseIsNaN.js | 12 + backend/node_modules/lodash/_baseIsNative.js | 47 + backend/node_modules/lodash/_baseIsRegExp.js | 18 + backend/node_modules/lodash/_baseIsSet.js | 18 + .../node_modules/lodash/_baseIsTypedArray.js | 60 + backend/node_modules/lodash/_baseIteratee.js | 31 + backend/node_modules/lodash/_baseKeys.js | 30 + backend/node_modules/lodash/_baseKeysIn.js | 33 + backend/node_modules/lodash/_baseLodash.js | 10 + backend/node_modules/lodash/_baseLt.js | 14 + backend/node_modules/lodash/_baseMap.js | 22 + backend/node_modules/lodash/_baseMatches.js | 22 + .../lodash/_baseMatchesProperty.js | 33 + backend/node_modules/lodash/_baseMean.js | 20 + backend/node_modules/lodash/_baseMerge.js | 42 + backend/node_modules/lodash/_baseMergeDeep.js | 94 + backend/node_modules/lodash/_baseNth.js | 20 + backend/node_modules/lodash/_baseOrderBy.js | 49 + backend/node_modules/lodash/_basePick.js | 19 + backend/node_modules/lodash/_basePickBy.js | 30 + backend/node_modules/lodash/_baseProperty.js | 14 + .../node_modules/lodash/_basePropertyDeep.js | 16 + .../node_modules/lodash/_basePropertyOf.js | 14 + backend/node_modules/lodash/_basePullAll.js | 51 + backend/node_modules/lodash/_basePullAt.js | 37 + backend/node_modules/lodash/_baseRandom.js | 18 + backend/node_modules/lodash/_baseRange.js | 28 + backend/node_modules/lodash/_baseReduce.js | 23 + backend/node_modules/lodash/_baseRepeat.js | 35 + backend/node_modules/lodash/_baseRest.js | 17 + backend/node_modules/lodash/_baseSample.js | 15 + .../node_modules/lodash/_baseSampleSize.js | 18 + backend/node_modules/lodash/_baseSet.js | 51 + backend/node_modules/lodash/_baseSetData.js | 17 + .../node_modules/lodash/_baseSetToString.js | 22 + backend/node_modules/lodash/_baseShuffle.js | 15 + backend/node_modules/lodash/_baseSlice.js | 31 + backend/node_modules/lodash/_baseSome.js | 22 + backend/node_modules/lodash/_baseSortBy.js | 21 + .../node_modules/lodash/_baseSortedIndex.js | 42 + .../node_modules/lodash/_baseSortedIndexBy.js | 67 + .../node_modules/lodash/_baseSortedUniq.js | 30 + backend/node_modules/lodash/_baseSum.js | 24 + backend/node_modules/lodash/_baseTimes.js | 20 + backend/node_modules/lodash/_baseToNumber.js | 24 + backend/node_modules/lodash/_baseToPairs.js | 18 + backend/node_modules/lodash/_baseToString.js | 37 + backend/node_modules/lodash/_baseTrim.js | 19 + backend/node_modules/lodash/_baseUnary.js | 14 + backend/node_modules/lodash/_baseUniq.js | 72 + backend/node_modules/lodash/_baseUnset.js | 52 + backend/node_modules/lodash/_baseUpdate.js | 18 + backend/node_modules/lodash/_baseValues.js | 19 + backend/node_modules/lodash/_baseWhile.js | 26 + .../node_modules/lodash/_baseWrapperValue.js | 25 + backend/node_modules/lodash/_baseXor.js | 36 + backend/node_modules/lodash/_baseZipObject.js | 23 + backend/node_modules/lodash/_cacheHas.js | 13 + .../lodash/_castArrayLikeObject.js | 14 + backend/node_modules/lodash/_castFunction.js | 14 + backend/node_modules/lodash/_castPath.js | 21 + backend/node_modules/lodash/_castRest.js | 14 + backend/node_modules/lodash/_castSlice.js | 18 + backend/node_modules/lodash/_charsEndIndex.js | 19 + .../node_modules/lodash/_charsStartIndex.js | 20 + .../node_modules/lodash/_cloneArrayBuffer.js | 16 + backend/node_modules/lodash/_cloneBuffer.js | 35 + backend/node_modules/lodash/_cloneDataView.js | 16 + backend/node_modules/lodash/_cloneRegExp.js | 17 + backend/node_modules/lodash/_cloneSymbol.js | 18 + .../node_modules/lodash/_cloneTypedArray.js | 16 + .../node_modules/lodash/_compareAscending.js | 41 + .../node_modules/lodash/_compareMultiple.js | 44 + backend/node_modules/lodash/_composeArgs.js | 39 + .../node_modules/lodash/_composeArgsRight.js | 41 + backend/node_modules/lodash/_copyArray.js | 20 + backend/node_modules/lodash/_copyObject.js | 40 + backend/node_modules/lodash/_copySymbols.js | 16 + backend/node_modules/lodash/_copySymbolsIn.js | 16 + backend/node_modules/lodash/_coreJsData.js | 6 + backend/node_modules/lodash/_countHolders.js | 21 + .../node_modules/lodash/_createAggregator.js | 23 + .../node_modules/lodash/_createAssigner.js | 37 + .../node_modules/lodash/_createBaseEach.js | 32 + backend/node_modules/lodash/_createBaseFor.js | 25 + backend/node_modules/lodash/_createBind.js | 28 + .../node_modules/lodash/_createCaseFirst.js | 33 + .../node_modules/lodash/_createCompounder.js | 24 + backend/node_modules/lodash/_createCtor.js | 37 + backend/node_modules/lodash/_createCurry.js | 46 + backend/node_modules/lodash/_createFind.js | 25 + backend/node_modules/lodash/_createFlow.js | 78 + backend/node_modules/lodash/_createHybrid.js | 92 + .../node_modules/lodash/_createInverter.js | 17 + .../lodash/_createMathOperation.js | 38 + backend/node_modules/lodash/_createOver.js | 27 + backend/node_modules/lodash/_createPadding.js | 33 + backend/node_modules/lodash/_createPartial.js | 43 + backend/node_modules/lodash/_createRange.js | 30 + backend/node_modules/lodash/_createRecurry.js | 56 + .../lodash/_createRelationalOperation.js | 20 + backend/node_modules/lodash/_createRound.js | 35 + backend/node_modules/lodash/_createSet.js | 19 + backend/node_modules/lodash/_createToPairs.js | 30 + backend/node_modules/lodash/_createWrap.js | 106 + .../lodash/_customDefaultsAssignIn.js | 29 + .../lodash/_customDefaultsMerge.js | 28 + .../node_modules/lodash/_customOmitClone.js | 16 + backend/node_modules/lodash/_deburrLetter.js | 71 + .../node_modules/lodash/_defineProperty.js | 11 + backend/node_modules/lodash/_equalArrays.js | 84 + backend/node_modules/lodash/_equalByTag.js | 112 + backend/node_modules/lodash/_equalObjects.js | 90 + .../node_modules/lodash/_escapeHtmlChar.js | 21 + .../node_modules/lodash/_escapeStringChar.js | 22 + backend/node_modules/lodash/_flatRest.js | 16 + backend/node_modules/lodash/_freeGlobal.js | 4 + backend/node_modules/lodash/_getAllKeys.js | 16 + backend/node_modules/lodash/_getAllKeysIn.js | 17 + backend/node_modules/lodash/_getData.js | 15 + backend/node_modules/lodash/_getFuncName.js | 31 + backend/node_modules/lodash/_getHolder.js | 13 + backend/node_modules/lodash/_getMapData.js | 18 + backend/node_modules/lodash/_getMatchData.js | 24 + backend/node_modules/lodash/_getNative.js | 17 + backend/node_modules/lodash/_getPrototype.js | 6 + backend/node_modules/lodash/_getRawTag.js | 46 + backend/node_modules/lodash/_getSymbols.js | 30 + backend/node_modules/lodash/_getSymbolsIn.js | 25 + backend/node_modules/lodash/_getTag.js | 58 + backend/node_modules/lodash/_getValue.js | 13 + backend/node_modules/lodash/_getView.js | 33 + .../node_modules/lodash/_getWrapDetails.js | 17 + backend/node_modules/lodash/_hasPath.js | 39 + backend/node_modules/lodash/_hasUnicode.js | 26 + .../node_modules/lodash/_hasUnicodeWord.js | 15 + backend/node_modules/lodash/_hashClear.js | 15 + backend/node_modules/lodash/_hashDelete.js | 17 + backend/node_modules/lodash/_hashGet.js | 30 + backend/node_modules/lodash/_hashHas.js | 23 + backend/node_modules/lodash/_hashSet.js | 23 + .../node_modules/lodash/_initCloneArray.js | 26 + .../node_modules/lodash/_initCloneByTag.js | 77 + .../node_modules/lodash/_initCloneObject.js | 18 + .../node_modules/lodash/_insertWrapDetails.js | 23 + backend/node_modules/lodash/_isFlattenable.js | 20 + backend/node_modules/lodash/_isIndex.js | 25 + .../node_modules/lodash/_isIterateeCall.js | 30 + backend/node_modules/lodash/_isKey.js | 29 + backend/node_modules/lodash/_isKeyable.js | 15 + backend/node_modules/lodash/_isLaziable.js | 28 + backend/node_modules/lodash/_isMaskable.js | 14 + backend/node_modules/lodash/_isMasked.js | 20 + backend/node_modules/lodash/_isPrototype.js | 18 + .../lodash/_isStrictComparable.js | 15 + .../node_modules/lodash/_iteratorToArray.js | 18 + backend/node_modules/lodash/_lazyClone.js | 23 + backend/node_modules/lodash/_lazyReverse.js | 23 + backend/node_modules/lodash/_lazyValue.js | 69 + .../node_modules/lodash/_listCacheClear.js | 13 + .../node_modules/lodash/_listCacheDelete.js | 35 + backend/node_modules/lodash/_listCacheGet.js | 19 + backend/node_modules/lodash/_listCacheHas.js | 16 + backend/node_modules/lodash/_listCacheSet.js | 26 + backend/node_modules/lodash/_mapCacheClear.js | 21 + .../node_modules/lodash/_mapCacheDelete.js | 18 + backend/node_modules/lodash/_mapCacheGet.js | 16 + backend/node_modules/lodash/_mapCacheHas.js | 16 + backend/node_modules/lodash/_mapCacheSet.js | 22 + backend/node_modules/lodash/_mapToArray.js | 18 + .../lodash/_matchesStrictComparable.js | 20 + backend/node_modules/lodash/_memoizeCapped.js | 26 + backend/node_modules/lodash/_mergeData.js | 90 + backend/node_modules/lodash/_metaMap.js | 6 + backend/node_modules/lodash/_nativeCreate.js | 6 + backend/node_modules/lodash/_nativeKeys.js | 6 + backend/node_modules/lodash/_nativeKeysIn.js | 20 + backend/node_modules/lodash/_nodeUtil.js | 30 + .../node_modules/lodash/_objectToString.js | 22 + backend/node_modules/lodash/_overArg.js | 15 + backend/node_modules/lodash/_overRest.js | 36 + backend/node_modules/lodash/_parent.js | 16 + backend/node_modules/lodash/_reEscape.js | 4 + backend/node_modules/lodash/_reEvaluate.js | 4 + backend/node_modules/lodash/_reInterpolate.js | 4 + backend/node_modules/lodash/_realNames.js | 4 + backend/node_modules/lodash/_reorder.js | 29 + .../node_modules/lodash/_replaceHolders.js | 29 + backend/node_modules/lodash/_root.js | 9 + backend/node_modules/lodash/_safeGet.js | 21 + backend/node_modules/lodash/_setCacheAdd.js | 19 + backend/node_modules/lodash/_setCacheHas.js | 14 + backend/node_modules/lodash/_setData.js | 20 + backend/node_modules/lodash/_setToArray.js | 18 + backend/node_modules/lodash/_setToPairs.js | 18 + backend/node_modules/lodash/_setToString.js | 14 + .../node_modules/lodash/_setWrapToString.js | 21 + backend/node_modules/lodash/_shortOut.js | 37 + backend/node_modules/lodash/_shuffleSelf.js | 28 + backend/node_modules/lodash/_stackClear.js | 15 + backend/node_modules/lodash/_stackDelete.js | 18 + backend/node_modules/lodash/_stackGet.js | 14 + backend/node_modules/lodash/_stackHas.js | 14 + backend/node_modules/lodash/_stackSet.js | 34 + backend/node_modules/lodash/_strictIndexOf.js | 23 + .../node_modules/lodash/_strictLastIndexOf.js | 21 + backend/node_modules/lodash/_stringSize.js | 18 + backend/node_modules/lodash/_stringToArray.js | 18 + backend/node_modules/lodash/_stringToPath.js | 27 + backend/node_modules/lodash/_toKey.js | 21 + backend/node_modules/lodash/_toSource.js | 26 + .../node_modules/lodash/_trimmedEndIndex.js | 19 + .../node_modules/lodash/_unescapeHtmlChar.js | 21 + backend/node_modules/lodash/_unicodeSize.js | 44 + .../node_modules/lodash/_unicodeToArray.js | 40 + backend/node_modules/lodash/_unicodeWords.js | 69 + .../node_modules/lodash/_updateWrapDetails.js | 46 + backend/node_modules/lodash/_wrapperClone.js | 23 + backend/node_modules/lodash/add.js | 22 + backend/node_modules/lodash/after.js | 42 + backend/node_modules/lodash/array.js | 67 + backend/node_modules/lodash/ary.js | 29 + backend/node_modules/lodash/assign.js | 58 + backend/node_modules/lodash/assignIn.js | 40 + backend/node_modules/lodash/assignInWith.js | 38 + backend/node_modules/lodash/assignWith.js | 37 + backend/node_modules/lodash/at.js | 23 + backend/node_modules/lodash/attempt.js | 35 + backend/node_modules/lodash/before.js | 40 + backend/node_modules/lodash/bind.js | 57 + backend/node_modules/lodash/bindAll.js | 41 + backend/node_modules/lodash/bindKey.js | 68 + backend/node_modules/lodash/camelCase.js | 29 + backend/node_modules/lodash/capitalize.js | 23 + backend/node_modules/lodash/castArray.js | 44 + backend/node_modules/lodash/ceil.js | 26 + backend/node_modules/lodash/chain.js | 38 + backend/node_modules/lodash/chunk.js | 50 + backend/node_modules/lodash/clamp.js | 39 + backend/node_modules/lodash/clone.js | 36 + backend/node_modules/lodash/cloneDeep.js | 29 + backend/node_modules/lodash/cloneDeepWith.js | 40 + backend/node_modules/lodash/cloneWith.js | 42 + backend/node_modules/lodash/collection.js | 30 + backend/node_modules/lodash/commit.js | 33 + backend/node_modules/lodash/compact.js | 31 + backend/node_modules/lodash/concat.js | 43 + backend/node_modules/lodash/cond.js | 60 + backend/node_modules/lodash/conforms.js | 35 + backend/node_modules/lodash/conformsTo.js | 32 + backend/node_modules/lodash/constant.js | 26 + backend/node_modules/lodash/core.js | 3877 +++ backend/node_modules/lodash/core.min.js | 30 + backend/node_modules/lodash/countBy.js | 40 + backend/node_modules/lodash/create.js | 43 + backend/node_modules/lodash/curry.js | 57 + backend/node_modules/lodash/curryRight.js | 54 + backend/node_modules/lodash/date.js | 3 + backend/node_modules/lodash/debounce.js | 191 + backend/node_modules/lodash/deburr.js | 45 + backend/node_modules/lodash/defaultTo.js | 25 + backend/node_modules/lodash/defaults.js | 64 + backend/node_modules/lodash/defaultsDeep.js | 30 + backend/node_modules/lodash/defer.js | 26 + backend/node_modules/lodash/delay.js | 28 + backend/node_modules/lodash/difference.js | 33 + backend/node_modules/lodash/differenceBy.js | 44 + backend/node_modules/lodash/differenceWith.js | 40 + backend/node_modules/lodash/divide.js | 22 + backend/node_modules/lodash/drop.js | 38 + backend/node_modules/lodash/dropRight.js | 39 + backend/node_modules/lodash/dropRightWhile.js | 45 + backend/node_modules/lodash/dropWhile.js | 45 + backend/node_modules/lodash/each.js | 1 + backend/node_modules/lodash/eachRight.js | 1 + backend/node_modules/lodash/endsWith.js | 43 + backend/node_modules/lodash/entries.js | 1 + backend/node_modules/lodash/entriesIn.js | 1 + backend/node_modules/lodash/eq.js | 37 + backend/node_modules/lodash/escape.js | 43 + backend/node_modules/lodash/escapeRegExp.js | 32 + backend/node_modules/lodash/every.js | 56 + backend/node_modules/lodash/extend.js | 1 + backend/node_modules/lodash/extendWith.js | 1 + backend/node_modules/lodash/fill.js | 45 + backend/node_modules/lodash/filter.js | 52 + backend/node_modules/lodash/find.js | 42 + backend/node_modules/lodash/findIndex.js | 55 + backend/node_modules/lodash/findKey.js | 44 + backend/node_modules/lodash/findLast.js | 25 + backend/node_modules/lodash/findLastIndex.js | 59 + backend/node_modules/lodash/findLastKey.js | 44 + backend/node_modules/lodash/first.js | 1 + backend/node_modules/lodash/flatMap.js | 29 + backend/node_modules/lodash/flatMapDeep.js | 31 + backend/node_modules/lodash/flatMapDepth.js | 31 + backend/node_modules/lodash/flatten.js | 22 + backend/node_modules/lodash/flattenDeep.js | 25 + backend/node_modules/lodash/flattenDepth.js | 33 + backend/node_modules/lodash/flip.js | 28 + backend/node_modules/lodash/floor.js | 26 + backend/node_modules/lodash/flow.js | 27 + backend/node_modules/lodash/flowRight.js | 26 + backend/node_modules/lodash/forEach.js | 41 + backend/node_modules/lodash/forEachRight.js | 31 + backend/node_modules/lodash/forIn.js | 39 + backend/node_modules/lodash/forInRight.js | 37 + backend/node_modules/lodash/forOwn.js | 36 + backend/node_modules/lodash/forOwnRight.js | 34 + backend/node_modules/lodash/fp.js | 2 + backend/node_modules/lodash/fp/F.js | 1 + backend/node_modules/lodash/fp/T.js | 1 + backend/node_modules/lodash/fp/__.js | 1 + .../node_modules/lodash/fp/_baseConvert.js | 569 + .../node_modules/lodash/fp/_convertBrowser.js | 18 + .../node_modules/lodash/fp/_falseOptions.js | 7 + backend/node_modules/lodash/fp/_mapping.js | 358 + backend/node_modules/lodash/fp/_util.js | 16 + backend/node_modules/lodash/fp/add.js | 5 + backend/node_modules/lodash/fp/after.js | 5 + backend/node_modules/lodash/fp/all.js | 1 + backend/node_modules/lodash/fp/allPass.js | 1 + backend/node_modules/lodash/fp/always.js | 1 + backend/node_modules/lodash/fp/any.js | 1 + backend/node_modules/lodash/fp/anyPass.js | 1 + backend/node_modules/lodash/fp/apply.js | 1 + backend/node_modules/lodash/fp/array.js | 2 + backend/node_modules/lodash/fp/ary.js | 5 + backend/node_modules/lodash/fp/assign.js | 5 + backend/node_modules/lodash/fp/assignAll.js | 5 + .../node_modules/lodash/fp/assignAllWith.js | 5 + backend/node_modules/lodash/fp/assignIn.js | 5 + backend/node_modules/lodash/fp/assignInAll.js | 5 + .../node_modules/lodash/fp/assignInAllWith.js | 5 + .../node_modules/lodash/fp/assignInWith.js | 5 + backend/node_modules/lodash/fp/assignWith.js | 5 + backend/node_modules/lodash/fp/assoc.js | 1 + backend/node_modules/lodash/fp/assocPath.js | 1 + backend/node_modules/lodash/fp/at.js | 5 + backend/node_modules/lodash/fp/attempt.js | 5 + backend/node_modules/lodash/fp/before.js | 5 + backend/node_modules/lodash/fp/bind.js | 5 + backend/node_modules/lodash/fp/bindAll.js | 5 + backend/node_modules/lodash/fp/bindKey.js | 5 + backend/node_modules/lodash/fp/camelCase.js | 5 + backend/node_modules/lodash/fp/capitalize.js | 5 + backend/node_modules/lodash/fp/castArray.js | 5 + backend/node_modules/lodash/fp/ceil.js | 5 + backend/node_modules/lodash/fp/chain.js | 5 + backend/node_modules/lodash/fp/chunk.js | 5 + backend/node_modules/lodash/fp/clamp.js | 5 + backend/node_modules/lodash/fp/clone.js | 5 + backend/node_modules/lodash/fp/cloneDeep.js | 5 + .../node_modules/lodash/fp/cloneDeepWith.js | 5 + backend/node_modules/lodash/fp/cloneWith.js | 5 + backend/node_modules/lodash/fp/collection.js | 2 + backend/node_modules/lodash/fp/commit.js | 5 + backend/node_modules/lodash/fp/compact.js | 5 + backend/node_modules/lodash/fp/complement.js | 1 + backend/node_modules/lodash/fp/compose.js | 1 + backend/node_modules/lodash/fp/concat.js | 5 + backend/node_modules/lodash/fp/cond.js | 5 + backend/node_modules/lodash/fp/conforms.js | 1 + backend/node_modules/lodash/fp/conformsTo.js | 5 + backend/node_modules/lodash/fp/constant.js | 5 + backend/node_modules/lodash/fp/contains.js | 1 + backend/node_modules/lodash/fp/convert.js | 18 + backend/node_modules/lodash/fp/countBy.js | 5 + backend/node_modules/lodash/fp/create.js | 5 + backend/node_modules/lodash/fp/curry.js | 5 + backend/node_modules/lodash/fp/curryN.js | 5 + backend/node_modules/lodash/fp/curryRight.js | 5 + backend/node_modules/lodash/fp/curryRightN.js | 5 + backend/node_modules/lodash/fp/date.js | 2 + backend/node_modules/lodash/fp/debounce.js | 5 + backend/node_modules/lodash/fp/deburr.js | 5 + backend/node_modules/lodash/fp/defaultTo.js | 5 + backend/node_modules/lodash/fp/defaults.js | 5 + backend/node_modules/lodash/fp/defaultsAll.js | 5 + .../node_modules/lodash/fp/defaultsDeep.js | 5 + .../node_modules/lodash/fp/defaultsDeepAll.js | 5 + backend/node_modules/lodash/fp/defer.js | 5 + backend/node_modules/lodash/fp/delay.js | 5 + backend/node_modules/lodash/fp/difference.js | 5 + .../node_modules/lodash/fp/differenceBy.js | 5 + .../node_modules/lodash/fp/differenceWith.js | 5 + backend/node_modules/lodash/fp/dissoc.js | 1 + backend/node_modules/lodash/fp/dissocPath.js | 1 + backend/node_modules/lodash/fp/divide.js | 5 + backend/node_modules/lodash/fp/drop.js | 5 + backend/node_modules/lodash/fp/dropLast.js | 1 + .../node_modules/lodash/fp/dropLastWhile.js | 1 + backend/node_modules/lodash/fp/dropRight.js | 5 + .../node_modules/lodash/fp/dropRightWhile.js | 5 + backend/node_modules/lodash/fp/dropWhile.js | 5 + backend/node_modules/lodash/fp/each.js | 1 + backend/node_modules/lodash/fp/eachRight.js | 1 + backend/node_modules/lodash/fp/endsWith.js | 5 + backend/node_modules/lodash/fp/entries.js | 1 + backend/node_modules/lodash/fp/entriesIn.js | 1 + backend/node_modules/lodash/fp/eq.js | 5 + backend/node_modules/lodash/fp/equals.js | 1 + backend/node_modules/lodash/fp/escape.js | 5 + .../node_modules/lodash/fp/escapeRegExp.js | 5 + backend/node_modules/lodash/fp/every.js | 5 + backend/node_modules/lodash/fp/extend.js | 1 + backend/node_modules/lodash/fp/extendAll.js | 1 + .../node_modules/lodash/fp/extendAllWith.js | 1 + backend/node_modules/lodash/fp/extendWith.js | 1 + backend/node_modules/lodash/fp/fill.js | 5 + backend/node_modules/lodash/fp/filter.js | 5 + backend/node_modules/lodash/fp/find.js | 5 + backend/node_modules/lodash/fp/findFrom.js | 5 + backend/node_modules/lodash/fp/findIndex.js | 5 + .../node_modules/lodash/fp/findIndexFrom.js | 5 + backend/node_modules/lodash/fp/findKey.js | 5 + backend/node_modules/lodash/fp/findLast.js | 5 + .../node_modules/lodash/fp/findLastFrom.js | 5 + .../node_modules/lodash/fp/findLastIndex.js | 5 + .../lodash/fp/findLastIndexFrom.js | 5 + backend/node_modules/lodash/fp/findLastKey.js | 5 + backend/node_modules/lodash/fp/first.js | 1 + backend/node_modules/lodash/fp/flatMap.js | 5 + backend/node_modules/lodash/fp/flatMapDeep.js | 5 + .../node_modules/lodash/fp/flatMapDepth.js | 5 + backend/node_modules/lodash/fp/flatten.js | 5 + backend/node_modules/lodash/fp/flattenDeep.js | 5 + .../node_modules/lodash/fp/flattenDepth.js | 5 + backend/node_modules/lodash/fp/flip.js | 5 + backend/node_modules/lodash/fp/floor.js | 5 + backend/node_modules/lodash/fp/flow.js | 5 + backend/node_modules/lodash/fp/flowRight.js | 5 + backend/node_modules/lodash/fp/forEach.js | 5 + .../node_modules/lodash/fp/forEachRight.js | 5 + backend/node_modules/lodash/fp/forIn.js | 5 + backend/node_modules/lodash/fp/forInRight.js | 5 + backend/node_modules/lodash/fp/forOwn.js | 5 + backend/node_modules/lodash/fp/forOwnRight.js | 5 + backend/node_modules/lodash/fp/fromPairs.js | 5 + backend/node_modules/lodash/fp/function.js | 2 + backend/node_modules/lodash/fp/functions.js | 5 + backend/node_modules/lodash/fp/functionsIn.js | 5 + backend/node_modules/lodash/fp/get.js | 5 + backend/node_modules/lodash/fp/getOr.js | 5 + backend/node_modules/lodash/fp/groupBy.js | 5 + backend/node_modules/lodash/fp/gt.js | 5 + backend/node_modules/lodash/fp/gte.js | 5 + backend/node_modules/lodash/fp/has.js | 5 + backend/node_modules/lodash/fp/hasIn.js | 5 + backend/node_modules/lodash/fp/head.js | 5 + backend/node_modules/lodash/fp/identical.js | 1 + backend/node_modules/lodash/fp/identity.js | 5 + backend/node_modules/lodash/fp/inRange.js | 5 + backend/node_modules/lodash/fp/includes.js | 5 + .../node_modules/lodash/fp/includesFrom.js | 5 + backend/node_modules/lodash/fp/indexBy.js | 1 + backend/node_modules/lodash/fp/indexOf.js | 5 + backend/node_modules/lodash/fp/indexOfFrom.js | 5 + backend/node_modules/lodash/fp/init.js | 1 + backend/node_modules/lodash/fp/initial.js | 5 + .../node_modules/lodash/fp/intersection.js | 5 + .../node_modules/lodash/fp/intersectionBy.js | 5 + .../lodash/fp/intersectionWith.js | 5 + backend/node_modules/lodash/fp/invert.js | 5 + backend/node_modules/lodash/fp/invertBy.js | 5 + backend/node_modules/lodash/fp/invertObj.js | 1 + backend/node_modules/lodash/fp/invoke.js | 5 + backend/node_modules/lodash/fp/invokeArgs.js | 5 + .../node_modules/lodash/fp/invokeArgsMap.js | 5 + backend/node_modules/lodash/fp/invokeMap.js | 5 + backend/node_modules/lodash/fp/isArguments.js | 5 + backend/node_modules/lodash/fp/isArray.js | 5 + .../node_modules/lodash/fp/isArrayBuffer.js | 5 + backend/node_modules/lodash/fp/isArrayLike.js | 5 + .../lodash/fp/isArrayLikeObject.js | 5 + backend/node_modules/lodash/fp/isBoolean.js | 5 + backend/node_modules/lodash/fp/isBuffer.js | 5 + backend/node_modules/lodash/fp/isDate.js | 5 + backend/node_modules/lodash/fp/isElement.js | 5 + backend/node_modules/lodash/fp/isEmpty.js | 5 + backend/node_modules/lodash/fp/isEqual.js | 5 + backend/node_modules/lodash/fp/isEqualWith.js | 5 + backend/node_modules/lodash/fp/isError.js | 5 + backend/node_modules/lodash/fp/isFinite.js | 5 + backend/node_modules/lodash/fp/isFunction.js | 5 + backend/node_modules/lodash/fp/isInteger.js | 5 + backend/node_modules/lodash/fp/isLength.js | 5 + backend/node_modules/lodash/fp/isMap.js | 5 + backend/node_modules/lodash/fp/isMatch.js | 5 + backend/node_modules/lodash/fp/isMatchWith.js | 5 + backend/node_modules/lodash/fp/isNaN.js | 5 + backend/node_modules/lodash/fp/isNative.js | 5 + backend/node_modules/lodash/fp/isNil.js | 5 + backend/node_modules/lodash/fp/isNull.js | 5 + backend/node_modules/lodash/fp/isNumber.js | 5 + backend/node_modules/lodash/fp/isObject.js | 5 + .../node_modules/lodash/fp/isObjectLike.js | 5 + .../node_modules/lodash/fp/isPlainObject.js | 5 + backend/node_modules/lodash/fp/isRegExp.js | 5 + .../node_modules/lodash/fp/isSafeInteger.js | 5 + backend/node_modules/lodash/fp/isSet.js | 5 + backend/node_modules/lodash/fp/isString.js | 5 + backend/node_modules/lodash/fp/isSymbol.js | 5 + .../node_modules/lodash/fp/isTypedArray.js | 5 + backend/node_modules/lodash/fp/isUndefined.js | 5 + backend/node_modules/lodash/fp/isWeakMap.js | 5 + backend/node_modules/lodash/fp/isWeakSet.js | 5 + backend/node_modules/lodash/fp/iteratee.js | 5 + backend/node_modules/lodash/fp/join.js | 5 + backend/node_modules/lodash/fp/juxt.js | 1 + backend/node_modules/lodash/fp/kebabCase.js | 5 + backend/node_modules/lodash/fp/keyBy.js | 5 + backend/node_modules/lodash/fp/keys.js | 5 + backend/node_modules/lodash/fp/keysIn.js | 5 + backend/node_modules/lodash/fp/lang.js | 2 + backend/node_modules/lodash/fp/last.js | 5 + backend/node_modules/lodash/fp/lastIndexOf.js | 5 + .../node_modules/lodash/fp/lastIndexOfFrom.js | 5 + backend/node_modules/lodash/fp/lowerCase.js | 5 + backend/node_modules/lodash/fp/lowerFirst.js | 5 + backend/node_modules/lodash/fp/lt.js | 5 + backend/node_modules/lodash/fp/lte.js | 5 + backend/node_modules/lodash/fp/map.js | 5 + backend/node_modules/lodash/fp/mapKeys.js | 5 + backend/node_modules/lodash/fp/mapValues.js | 5 + backend/node_modules/lodash/fp/matches.js | 1 + .../node_modules/lodash/fp/matchesProperty.js | 5 + backend/node_modules/lodash/fp/math.js | 2 + backend/node_modules/lodash/fp/max.js | 5 + backend/node_modules/lodash/fp/maxBy.js | 5 + backend/node_modules/lodash/fp/mean.js | 5 + backend/node_modules/lodash/fp/meanBy.js | 5 + backend/node_modules/lodash/fp/memoize.js | 5 + backend/node_modules/lodash/fp/merge.js | 5 + backend/node_modules/lodash/fp/mergeAll.js | 5 + .../node_modules/lodash/fp/mergeAllWith.js | 5 + backend/node_modules/lodash/fp/mergeWith.js | 5 + backend/node_modules/lodash/fp/method.js | 5 + backend/node_modules/lodash/fp/methodOf.js | 5 + backend/node_modules/lodash/fp/min.js | 5 + backend/node_modules/lodash/fp/minBy.js | 5 + backend/node_modules/lodash/fp/mixin.js | 5 + backend/node_modules/lodash/fp/multiply.js | 5 + backend/node_modules/lodash/fp/nAry.js | 1 + backend/node_modules/lodash/fp/negate.js | 5 + backend/node_modules/lodash/fp/next.js | 5 + backend/node_modules/lodash/fp/noop.js | 5 + backend/node_modules/lodash/fp/now.js | 5 + backend/node_modules/lodash/fp/nth.js | 5 + backend/node_modules/lodash/fp/nthArg.js | 5 + backend/node_modules/lodash/fp/number.js | 2 + backend/node_modules/lodash/fp/object.js | 2 + backend/node_modules/lodash/fp/omit.js | 5 + backend/node_modules/lodash/fp/omitAll.js | 1 + backend/node_modules/lodash/fp/omitBy.js | 5 + backend/node_modules/lodash/fp/once.js | 5 + backend/node_modules/lodash/fp/orderBy.js | 5 + backend/node_modules/lodash/fp/over.js | 5 + backend/node_modules/lodash/fp/overArgs.js | 5 + backend/node_modules/lodash/fp/overEvery.js | 5 + backend/node_modules/lodash/fp/overSome.js | 5 + backend/node_modules/lodash/fp/pad.js | 5 + backend/node_modules/lodash/fp/padChars.js | 5 + backend/node_modules/lodash/fp/padCharsEnd.js | 5 + .../node_modules/lodash/fp/padCharsStart.js | 5 + backend/node_modules/lodash/fp/padEnd.js | 5 + backend/node_modules/lodash/fp/padStart.js | 5 + backend/node_modules/lodash/fp/parseInt.js | 5 + backend/node_modules/lodash/fp/partial.js | 5 + .../node_modules/lodash/fp/partialRight.js | 5 + backend/node_modules/lodash/fp/partition.js | 5 + backend/node_modules/lodash/fp/path.js | 1 + backend/node_modules/lodash/fp/pathEq.js | 1 + backend/node_modules/lodash/fp/pathOr.js | 1 + backend/node_modules/lodash/fp/paths.js | 1 + backend/node_modules/lodash/fp/pick.js | 5 + backend/node_modules/lodash/fp/pickAll.js | 1 + backend/node_modules/lodash/fp/pickBy.js | 5 + backend/node_modules/lodash/fp/pipe.js | 1 + backend/node_modules/lodash/fp/placeholder.js | 6 + backend/node_modules/lodash/fp/plant.js | 5 + backend/node_modules/lodash/fp/pluck.js | 1 + backend/node_modules/lodash/fp/prop.js | 1 + backend/node_modules/lodash/fp/propEq.js | 1 + backend/node_modules/lodash/fp/propOr.js | 1 + backend/node_modules/lodash/fp/property.js | 1 + backend/node_modules/lodash/fp/propertyOf.js | 5 + backend/node_modules/lodash/fp/props.js | 1 + backend/node_modules/lodash/fp/pull.js | 5 + backend/node_modules/lodash/fp/pullAll.js | 5 + backend/node_modules/lodash/fp/pullAllBy.js | 5 + backend/node_modules/lodash/fp/pullAllWith.js | 5 + backend/node_modules/lodash/fp/pullAt.js | 5 + backend/node_modules/lodash/fp/random.js | 5 + backend/node_modules/lodash/fp/range.js | 5 + backend/node_modules/lodash/fp/rangeRight.js | 5 + backend/node_modules/lodash/fp/rangeStep.js | 5 + .../node_modules/lodash/fp/rangeStepRight.js | 5 + backend/node_modules/lodash/fp/rearg.js | 5 + backend/node_modules/lodash/fp/reduce.js | 5 + backend/node_modules/lodash/fp/reduceRight.js | 5 + backend/node_modules/lodash/fp/reject.js | 5 + backend/node_modules/lodash/fp/remove.js | 5 + backend/node_modules/lodash/fp/repeat.js | 5 + backend/node_modules/lodash/fp/replace.js | 5 + backend/node_modules/lodash/fp/rest.js | 5 + backend/node_modules/lodash/fp/restFrom.js | 5 + backend/node_modules/lodash/fp/result.js | 5 + backend/node_modules/lodash/fp/reverse.js | 5 + backend/node_modules/lodash/fp/round.js | 5 + backend/node_modules/lodash/fp/sample.js | 5 + backend/node_modules/lodash/fp/sampleSize.js | 5 + backend/node_modules/lodash/fp/seq.js | 2 + backend/node_modules/lodash/fp/set.js | 5 + backend/node_modules/lodash/fp/setWith.js | 5 + backend/node_modules/lodash/fp/shuffle.js | 5 + backend/node_modules/lodash/fp/size.js | 5 + backend/node_modules/lodash/fp/slice.js | 5 + backend/node_modules/lodash/fp/snakeCase.js | 5 + backend/node_modules/lodash/fp/some.js | 5 + backend/node_modules/lodash/fp/sortBy.js | 5 + backend/node_modules/lodash/fp/sortedIndex.js | 5 + .../node_modules/lodash/fp/sortedIndexBy.js | 5 + .../node_modules/lodash/fp/sortedIndexOf.js | 5 + .../node_modules/lodash/fp/sortedLastIndex.js | 5 + .../lodash/fp/sortedLastIndexBy.js | 5 + .../lodash/fp/sortedLastIndexOf.js | 5 + backend/node_modules/lodash/fp/sortedUniq.js | 5 + .../node_modules/lodash/fp/sortedUniqBy.js | 5 + backend/node_modules/lodash/fp/split.js | 5 + backend/node_modules/lodash/fp/spread.js | 5 + backend/node_modules/lodash/fp/spreadFrom.js | 5 + backend/node_modules/lodash/fp/startCase.js | 5 + backend/node_modules/lodash/fp/startsWith.js | 5 + backend/node_modules/lodash/fp/string.js | 2 + backend/node_modules/lodash/fp/stubArray.js | 5 + backend/node_modules/lodash/fp/stubFalse.js | 5 + backend/node_modules/lodash/fp/stubObject.js | 5 + backend/node_modules/lodash/fp/stubString.js | 5 + backend/node_modules/lodash/fp/stubTrue.js | 5 + backend/node_modules/lodash/fp/subtract.js | 5 + backend/node_modules/lodash/fp/sum.js | 5 + backend/node_modules/lodash/fp/sumBy.js | 5 + .../lodash/fp/symmetricDifference.js | 1 + .../lodash/fp/symmetricDifferenceBy.js | 1 + .../lodash/fp/symmetricDifferenceWith.js | 1 + backend/node_modules/lodash/fp/tail.js | 5 + backend/node_modules/lodash/fp/take.js | 5 + backend/node_modules/lodash/fp/takeLast.js | 1 + .../node_modules/lodash/fp/takeLastWhile.js | 1 + backend/node_modules/lodash/fp/takeRight.js | 5 + .../node_modules/lodash/fp/takeRightWhile.js | 5 + backend/node_modules/lodash/fp/takeWhile.js | 5 + backend/node_modules/lodash/fp/tap.js | 5 + backend/node_modules/lodash/fp/template.js | 5 + .../lodash/fp/templateSettings.js | 5 + backend/node_modules/lodash/fp/throttle.js | 5 + backend/node_modules/lodash/fp/thru.js | 5 + backend/node_modules/lodash/fp/times.js | 5 + backend/node_modules/lodash/fp/toArray.js | 5 + backend/node_modules/lodash/fp/toFinite.js | 5 + backend/node_modules/lodash/fp/toInteger.js | 5 + backend/node_modules/lodash/fp/toIterator.js | 5 + backend/node_modules/lodash/fp/toJSON.js | 5 + backend/node_modules/lodash/fp/toLength.js | 5 + backend/node_modules/lodash/fp/toLower.js | 5 + backend/node_modules/lodash/fp/toNumber.js | 5 + backend/node_modules/lodash/fp/toPairs.js | 5 + backend/node_modules/lodash/fp/toPairsIn.js | 5 + backend/node_modules/lodash/fp/toPath.js | 5 + .../node_modules/lodash/fp/toPlainObject.js | 5 + .../node_modules/lodash/fp/toSafeInteger.js | 5 + backend/node_modules/lodash/fp/toString.js | 5 + backend/node_modules/lodash/fp/toUpper.js | 5 + backend/node_modules/lodash/fp/transform.js | 5 + backend/node_modules/lodash/fp/trim.js | 5 + backend/node_modules/lodash/fp/trimChars.js | 5 + .../node_modules/lodash/fp/trimCharsEnd.js | 5 + .../node_modules/lodash/fp/trimCharsStart.js | 5 + backend/node_modules/lodash/fp/trimEnd.js | 5 + backend/node_modules/lodash/fp/trimStart.js | 5 + backend/node_modules/lodash/fp/truncate.js | 5 + backend/node_modules/lodash/fp/unapply.js | 1 + backend/node_modules/lodash/fp/unary.js | 5 + backend/node_modules/lodash/fp/unescape.js | 5 + backend/node_modules/lodash/fp/union.js | 5 + backend/node_modules/lodash/fp/unionBy.js | 5 + backend/node_modules/lodash/fp/unionWith.js | 5 + backend/node_modules/lodash/fp/uniq.js | 5 + backend/node_modules/lodash/fp/uniqBy.js | 5 + backend/node_modules/lodash/fp/uniqWith.js | 5 + backend/node_modules/lodash/fp/uniqueId.js | 5 + backend/node_modules/lodash/fp/unnest.js | 1 + backend/node_modules/lodash/fp/unset.js | 5 + backend/node_modules/lodash/fp/unzip.js | 5 + backend/node_modules/lodash/fp/unzipWith.js | 5 + backend/node_modules/lodash/fp/update.js | 5 + backend/node_modules/lodash/fp/updateWith.js | 5 + backend/node_modules/lodash/fp/upperCase.js | 5 + backend/node_modules/lodash/fp/upperFirst.js | 5 + backend/node_modules/lodash/fp/useWith.js | 1 + backend/node_modules/lodash/fp/util.js | 2 + backend/node_modules/lodash/fp/value.js | 5 + backend/node_modules/lodash/fp/valueOf.js | 5 + backend/node_modules/lodash/fp/values.js | 5 + backend/node_modules/lodash/fp/valuesIn.js | 5 + backend/node_modules/lodash/fp/where.js | 1 + backend/node_modules/lodash/fp/whereEq.js | 1 + backend/node_modules/lodash/fp/without.js | 5 + backend/node_modules/lodash/fp/words.js | 5 + backend/node_modules/lodash/fp/wrap.js | 5 + backend/node_modules/lodash/fp/wrapperAt.js | 5 + .../node_modules/lodash/fp/wrapperChain.js | 5 + .../node_modules/lodash/fp/wrapperLodash.js | 5 + .../node_modules/lodash/fp/wrapperReverse.js | 5 + .../node_modules/lodash/fp/wrapperValue.js | 5 + backend/node_modules/lodash/fp/xor.js | 5 + backend/node_modules/lodash/fp/xorBy.js | 5 + backend/node_modules/lodash/fp/xorWith.js | 5 + backend/node_modules/lodash/fp/zip.js | 5 + backend/node_modules/lodash/fp/zipAll.js | 5 + backend/node_modules/lodash/fp/zipObj.js | 1 + backend/node_modules/lodash/fp/zipObject.js | 5 + .../node_modules/lodash/fp/zipObjectDeep.js | 5 + backend/node_modules/lodash/fp/zipWith.js | 5 + backend/node_modules/lodash/fromPairs.js | 30 + backend/node_modules/lodash/function.js | 25 + backend/node_modules/lodash/functions.js | 31 + backend/node_modules/lodash/functionsIn.js | 31 + backend/node_modules/lodash/get.js | 33 + backend/node_modules/lodash/groupBy.js | 41 + backend/node_modules/lodash/gt.js | 29 + backend/node_modules/lodash/gte.js | 30 + backend/node_modules/lodash/has.js | 35 + backend/node_modules/lodash/hasIn.js | 34 + backend/node_modules/lodash/head.js | 23 + backend/node_modules/lodash/identity.js | 21 + backend/node_modules/lodash/inRange.js | 55 + backend/node_modules/lodash/includes.js | 53 + backend/node_modules/lodash/index.js | 1 + backend/node_modules/lodash/indexOf.js | 42 + backend/node_modules/lodash/initial.js | 22 + backend/node_modules/lodash/intersection.js | 30 + backend/node_modules/lodash/intersectionBy.js | 45 + .../node_modules/lodash/intersectionWith.js | 41 + backend/node_modules/lodash/invert.js | 42 + backend/node_modules/lodash/invertBy.js | 56 + backend/node_modules/lodash/invoke.js | 24 + backend/node_modules/lodash/invokeMap.js | 41 + backend/node_modules/lodash/isArguments.js | 36 + backend/node_modules/lodash/isArray.js | 26 + backend/node_modules/lodash/isArrayBuffer.js | 27 + backend/node_modules/lodash/isArrayLike.js | 33 + .../node_modules/lodash/isArrayLikeObject.js | 33 + backend/node_modules/lodash/isBoolean.js | 29 + backend/node_modules/lodash/isBuffer.js | 38 + backend/node_modules/lodash/isDate.js | 27 + backend/node_modules/lodash/isElement.js | 25 + backend/node_modules/lodash/isEmpty.js | 77 + backend/node_modules/lodash/isEqual.js | 35 + backend/node_modules/lodash/isEqualWith.js | 41 + backend/node_modules/lodash/isError.js | 36 + backend/node_modules/lodash/isFinite.js | 36 + backend/node_modules/lodash/isFunction.js | 37 + backend/node_modules/lodash/isInteger.js | 33 + backend/node_modules/lodash/isLength.js | 35 + backend/node_modules/lodash/isMap.js | 27 + backend/node_modules/lodash/isMatch.js | 36 + backend/node_modules/lodash/isMatchWith.js | 41 + backend/node_modules/lodash/isNaN.js | 38 + backend/node_modules/lodash/isNative.js | 40 + backend/node_modules/lodash/isNil.js | 25 + backend/node_modules/lodash/isNull.js | 22 + backend/node_modules/lodash/isNumber.js | 38 + backend/node_modules/lodash/isObject.js | 31 + backend/node_modules/lodash/isObjectLike.js | 29 + backend/node_modules/lodash/isPlainObject.js | 62 + backend/node_modules/lodash/isRegExp.js | 27 + backend/node_modules/lodash/isSafeInteger.js | 37 + backend/node_modules/lodash/isSet.js | 27 + backend/node_modules/lodash/isString.js | 30 + backend/node_modules/lodash/isSymbol.js | 29 + backend/node_modules/lodash/isTypedArray.js | 27 + backend/node_modules/lodash/isUndefined.js | 22 + backend/node_modules/lodash/isWeakMap.js | 28 + backend/node_modules/lodash/isWeakSet.js | 28 + backend/node_modules/lodash/iteratee.js | 53 + backend/node_modules/lodash/join.js | 26 + backend/node_modules/lodash/kebabCase.js | 28 + backend/node_modules/lodash/keyBy.js | 36 + backend/node_modules/lodash/keys.js | 37 + backend/node_modules/lodash/keysIn.js | 32 + backend/node_modules/lodash/lang.js | 58 + backend/node_modules/lodash/last.js | 20 + backend/node_modules/lodash/lastIndexOf.js | 46 + backend/node_modules/lodash/lodash.js | 17259 ++++++++++++ backend/node_modules/lodash/lodash.min.js | 136 + backend/node_modules/lodash/lowerCase.js | 27 + backend/node_modules/lodash/lowerFirst.js | 22 + backend/node_modules/lodash/lt.js | 29 + backend/node_modules/lodash/lte.js | 30 + backend/node_modules/lodash/map.js | 53 + backend/node_modules/lodash/mapKeys.js | 36 + backend/node_modules/lodash/mapValues.js | 43 + backend/node_modules/lodash/matches.js | 46 + .../node_modules/lodash/matchesProperty.js | 44 + backend/node_modules/lodash/math.js | 17 + backend/node_modules/lodash/max.js | 29 + backend/node_modules/lodash/maxBy.js | 34 + backend/node_modules/lodash/mean.js | 22 + backend/node_modules/lodash/meanBy.js | 31 + backend/node_modules/lodash/memoize.js | 73 + backend/node_modules/lodash/merge.js | 39 + backend/node_modules/lodash/mergeWith.js | 39 + backend/node_modules/lodash/method.js | 34 + backend/node_modules/lodash/methodOf.js | 33 + backend/node_modules/lodash/min.js | 29 + backend/node_modules/lodash/minBy.js | 34 + backend/node_modules/lodash/mixin.js | 74 + backend/node_modules/lodash/multiply.js | 22 + backend/node_modules/lodash/negate.js | 40 + backend/node_modules/lodash/next.js | 35 + backend/node_modules/lodash/noop.js | 17 + backend/node_modules/lodash/now.js | 23 + backend/node_modules/lodash/nth.js | 29 + backend/node_modules/lodash/nthArg.js | 32 + backend/node_modules/lodash/number.js | 5 + backend/node_modules/lodash/object.js | 49 + backend/node_modules/lodash/omit.js | 57 + backend/node_modules/lodash/omitBy.js | 29 + backend/node_modules/lodash/once.js | 25 + backend/node_modules/lodash/orderBy.js | 47 + backend/node_modules/lodash/over.js | 24 + backend/node_modules/lodash/overArgs.js | 61 + backend/node_modules/lodash/overEvery.js | 34 + backend/node_modules/lodash/overSome.js | 37 + backend/node_modules/lodash/package.json | 19 + backend/node_modules/lodash/pad.js | 49 + backend/node_modules/lodash/padEnd.js | 39 + backend/node_modules/lodash/padStart.js | 39 + backend/node_modules/lodash/parseInt.js | 43 + backend/node_modules/lodash/partial.js | 50 + backend/node_modules/lodash/partialRight.js | 49 + backend/node_modules/lodash/partition.js | 43 + backend/node_modules/lodash/pick.js | 25 + backend/node_modules/lodash/pickBy.js | 37 + backend/node_modules/lodash/plant.js | 48 + backend/node_modules/lodash/property.js | 32 + backend/node_modules/lodash/propertyOf.js | 30 + backend/node_modules/lodash/pull.js | 29 + backend/node_modules/lodash/pullAll.js | 29 + backend/node_modules/lodash/pullAllBy.js | 33 + backend/node_modules/lodash/pullAllWith.js | 32 + backend/node_modules/lodash/pullAt.js | 43 + backend/node_modules/lodash/random.js | 91 + backend/node_modules/lodash/range.js | 46 + backend/node_modules/lodash/rangeRight.js | 41 + backend/node_modules/lodash/rearg.js | 33 + backend/node_modules/lodash/reduce.js | 51 + backend/node_modules/lodash/reduceRight.js | 36 + backend/node_modules/lodash/reject.js | 46 + backend/node_modules/lodash/remove.js | 53 + backend/node_modules/lodash/repeat.js | 37 + backend/node_modules/lodash/replace.js | 29 + backend/node_modules/lodash/rest.js | 40 + backend/node_modules/lodash/result.js | 56 + backend/node_modules/lodash/reverse.js | 34 + backend/node_modules/lodash/round.js | 26 + backend/node_modules/lodash/sample.js | 24 + backend/node_modules/lodash/sampleSize.js | 37 + backend/node_modules/lodash/seq.js | 16 + backend/node_modules/lodash/set.js | 35 + backend/node_modules/lodash/setWith.js | 32 + backend/node_modules/lodash/shuffle.js | 25 + backend/node_modules/lodash/size.js | 46 + backend/node_modules/lodash/slice.js | 37 + backend/node_modules/lodash/snakeCase.js | 28 + backend/node_modules/lodash/some.js | 51 + backend/node_modules/lodash/sortBy.js | 48 + backend/node_modules/lodash/sortedIndex.js | 24 + backend/node_modules/lodash/sortedIndexBy.js | 33 + backend/node_modules/lodash/sortedIndexOf.js | 31 + .../node_modules/lodash/sortedLastIndex.js | 25 + .../node_modules/lodash/sortedLastIndexBy.js | 33 + .../node_modules/lodash/sortedLastIndexOf.js | 31 + backend/node_modules/lodash/sortedUniq.js | 24 + backend/node_modules/lodash/sortedUniqBy.js | 26 + backend/node_modules/lodash/split.js | 52 + backend/node_modules/lodash/spread.js | 63 + backend/node_modules/lodash/startCase.js | 29 + backend/node_modules/lodash/startsWith.js | 39 + backend/node_modules/lodash/string.js | 33 + backend/node_modules/lodash/stubArray.js | 23 + backend/node_modules/lodash/stubFalse.js | 18 + backend/node_modules/lodash/stubObject.js | 23 + backend/node_modules/lodash/stubString.js | 18 + backend/node_modules/lodash/stubTrue.js | 18 + backend/node_modules/lodash/subtract.js | 22 + backend/node_modules/lodash/sum.js | 24 + backend/node_modules/lodash/sumBy.js | 33 + backend/node_modules/lodash/tail.js | 22 + backend/node_modules/lodash/take.js | 37 + backend/node_modules/lodash/takeRight.js | 39 + backend/node_modules/lodash/takeRightWhile.js | 45 + backend/node_modules/lodash/takeWhile.js | 45 + backend/node_modules/lodash/tap.js | 29 + backend/node_modules/lodash/template.js | 284 + .../node_modules/lodash/templateSettings.js | 71 + backend/node_modules/lodash/throttle.js | 69 + backend/node_modules/lodash/thru.js | 28 + backend/node_modules/lodash/times.js | 51 + backend/node_modules/lodash/toArray.js | 58 + backend/node_modules/lodash/toFinite.js | 42 + backend/node_modules/lodash/toInteger.js | 36 + backend/node_modules/lodash/toIterator.js | 23 + backend/node_modules/lodash/toJSON.js | 1 + backend/node_modules/lodash/toLength.js | 38 + backend/node_modules/lodash/toLower.js | 28 + backend/node_modules/lodash/toNumber.js | 64 + backend/node_modules/lodash/toPairs.js | 30 + backend/node_modules/lodash/toPairsIn.js | 30 + backend/node_modules/lodash/toPath.js | 33 + backend/node_modules/lodash/toPlainObject.js | 32 + backend/node_modules/lodash/toSafeInteger.js | 37 + backend/node_modules/lodash/toString.js | 28 + backend/node_modules/lodash/toUpper.js | 28 + backend/node_modules/lodash/transform.js | 65 + backend/node_modules/lodash/trim.js | 47 + backend/node_modules/lodash/trimEnd.js | 41 + backend/node_modules/lodash/trimStart.js | 43 + backend/node_modules/lodash/truncate.js | 111 + backend/node_modules/lodash/unary.js | 22 + backend/node_modules/lodash/unescape.js | 34 + backend/node_modules/lodash/union.js | 26 + backend/node_modules/lodash/unionBy.js | 39 + backend/node_modules/lodash/unionWith.js | 34 + backend/node_modules/lodash/uniq.js | 25 + backend/node_modules/lodash/uniqBy.js | 31 + backend/node_modules/lodash/uniqWith.js | 28 + backend/node_modules/lodash/uniqueId.js | 28 + backend/node_modules/lodash/unset.js | 34 + backend/node_modules/lodash/unzip.js | 45 + backend/node_modules/lodash/unzipWith.js | 39 + backend/node_modules/lodash/update.js | 35 + backend/node_modules/lodash/updateWith.js | 33 + backend/node_modules/lodash/upperCase.js | 27 + backend/node_modules/lodash/upperFirst.js | 22 + backend/node_modules/lodash/util.js | 34 + backend/node_modules/lodash/value.js | 1 + backend/node_modules/lodash/valueOf.js | 1 + backend/node_modules/lodash/values.js | 34 + backend/node_modules/lodash/valuesIn.js | 32 + backend/node_modules/lodash/without.js | 31 + backend/node_modules/lodash/words.js | 35 + backend/node_modules/lodash/wrap.js | 30 + backend/node_modules/lodash/wrapperAt.js | 48 + backend/node_modules/lodash/wrapperChain.js | 34 + backend/node_modules/lodash/wrapperLodash.js | 147 + backend/node_modules/lodash/wrapperReverse.js | 44 + backend/node_modules/lodash/wrapperValue.js | 21 + backend/node_modules/lodash/xor.js | 28 + backend/node_modules/lodash/xorBy.js | 39 + backend/node_modules/lodash/xorWith.js | 34 + backend/node_modules/lodash/zip.js | 22 + backend/node_modules/lodash/zipObject.js | 24 + backend/node_modules/lodash/zipObjectDeep.js | 23 + backend/node_modules/lodash/zipWith.js | 32 + backend/node_modules/magic-string/LICENSE | 7 + backend/node_modules/magic-string/README.md | 325 + .../node_modules/magic-string/package.json | 67 + backend/node_modules/nanoid/LICENSE | 20 + backend/node_modules/nanoid/README.md | 36 + .../nanoid/async/index.browser.cjs | 43 + .../nanoid/async/index.browser.js | 43 + backend/node_modules/nanoid/async/index.cjs | 47 + backend/node_modules/nanoid/async/index.d.ts | 56 + backend/node_modules/nanoid/async/index.js | 47 + .../node_modules/nanoid/async/index.native.js | 35 + .../node_modules/nanoid/async/package.json | 12 + backend/node_modules/nanoid/bin/nanoid.cjs | 55 + backend/node_modules/nanoid/index.browser.cjs | 45 + backend/node_modules/nanoid/index.browser.js | 45 + backend/node_modules/nanoid/index.cjs | 58 + backend/node_modules/nanoid/index.d.cts | 91 + backend/node_modules/nanoid/index.d.ts | 91 + backend/node_modules/nanoid/index.js | 63 + backend/node_modules/nanoid/nanoid.js | 1 + .../node_modules/nanoid/non-secure/index.cjs | 24 + .../node_modules/nanoid/non-secure/index.d.ts | 33 + .../node_modules/nanoid/non-secure/index.js | 24 + .../nanoid/non-secure/package.json | 6 + backend/node_modules/nanoid/package.json | 89 + .../nanoid/url-alphabet/index.cjs | 4 + .../node_modules/nanoid/url-alphabet/index.js | 4 + .../nanoid/url-alphabet/package.json | 6 + backend/node_modules/oauth4webapi/LICENSE.md | 21 + backend/node_modules/oauth4webapi/README.md | 104 + .../node_modules/oauth4webapi/package.json | 105 + backend/node_modules/openid-client/LICENSE.md | 21 + backend/node_modules/openid-client/README.md | 259 + .../node_modules/openid-client/package.json | 122 + backend/node_modules/picocolors/LICENSE | 15 + backend/node_modules/picocolors/README.md | 21 + backend/node_modules/picocolors/package.json | 25 + .../picocolors/picocolors.browser.js | 4 + .../node_modules/picocolors/picocolors.d.ts | 5 + backend/node_modules/picocolors/picocolors.js | 75 + backend/node_modules/picocolors/types.d.ts | 51 + backend/node_modules/postcss/LICENSE | 20 + backend/node_modules/postcss/README.md | 24 + backend/node_modules/postcss/package.json | 88 + backend/node_modules/source-map-js/LICENSE | 28 + backend/node_modules/source-map-js/README.md | 765 + .../node_modules/source-map-js/package.json | 71 + .../source-map-js/source-map.d.ts | 104 + .../node_modules/source-map-js/source-map.js | 8 + backend/node_modules/vue/LICENSE | 21 + backend/node_modules/vue/README.md | 58 + .../vue/compiler-sfc/index.browser.js | 1 + .../vue/compiler-sfc/index.browser.mjs | 1 + .../node_modules/vue/compiler-sfc/index.d.mts | 1 + .../node_modules/vue/compiler-sfc/index.d.ts | 1 + .../node_modules/vue/compiler-sfc/index.js | 3 + .../node_modules/vue/compiler-sfc/index.mjs | 3 + .../vue/compiler-sfc/package.json | 4 + .../vue/compiler-sfc/register-ts.js | 3 + backend/node_modules/vue/index.js | 7 + backend/node_modules/vue/index.mjs | 1 + .../node_modules/vue/jsx-runtime/index.d.ts | 25 + backend/node_modules/vue/jsx-runtime/index.js | 15 + .../node_modules/vue/jsx-runtime/index.mjs | 12 + .../node_modules/vue/jsx-runtime/package.json | 5 + backend/node_modules/vue/jsx.d.ts | 22 + backend/node_modules/vue/package.json | 112 + .../vue/server-renderer/index.d.mts | 1 + .../vue/server-renderer/index.d.ts | 1 + .../node_modules/vue/server-renderer/index.js | 1 + .../vue/server-renderer/index.mjs | 1 + .../vue/server-renderer/package.json | 4 + backend/package-lock.json | 379 + backend/package.json | 5 + backend/poetry.lock | 3442 +++ frontend/.env.example | 4 + frontend/.npmrc | 1 - frontend/app/app.vue | 16 +- frontend/app/components/AuthMenu.client.vue | 71 + frontend/app/middleware/auth.ts | 28 + frontend/app/pages/login.vue | 15 + frontend/app/pages/my-runs.vue | 14 + frontend/app/plugins/auth0.client.ts | 36 + frontend/nuxt.config.ts | 3 + frontend/package-lock.json | 97 + frontend/package.json | 1 + kustomize/config/biosim-gke/api.env | 3 + kustomize/config/biosim-gke/frontend.env | 4 + package-lock.json | 6 + 1351 files changed, 103400 insertions(+), 12 deletions(-) create mode 100644 backend/biosim_server/common/auth/auth0.py create mode 120000 backend/node_modules/.bin/nanoid create mode 120000 backend/node_modules/.bin/parser create mode 100644 backend/node_modules/.package-lock.json create mode 100644 backend/node_modules/@auth0/auth0-auth-js/LICENSE create mode 100644 backend/node_modules/@auth0/auth0-auth-js/README.md create mode 100644 backend/node_modules/@auth0/auth0-auth-js/package.json create mode 100644 backend/node_modules/@auth0/auth0-spa-js/LICENSE create mode 100644 backend/node_modules/@auth0/auth0-spa-js/README.md create mode 100644 backend/node_modules/@auth0/auth0-spa-js/package.json create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.utils.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/TokenExchange.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/api.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-localstorage.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-manager.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-memory.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/cache/index.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/cache/key-manifest.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/cache/shared.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/constants.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/dpop/dpop.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/dpop/storage.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/dpop/utils.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/errors.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/fetcher.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/global.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/http.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/index.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/jwt.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/lock.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaApiClient.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaContextManager.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/constants.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/errors.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/index.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/types.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/mfa/utils.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/myaccount/MyAccountApiClient.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/myaccount/index.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/myaccount/types.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/passkey/PasskeyApiClient.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/passkey/errors.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/passkey/index.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/passkey/types.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/promise-utils.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/scope.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/storage.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/transaction-manager.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/utils.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/version.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/worker/__mocks__/token.worker.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/worker/token.worker.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.types.ts create mode 100644 backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.utils.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/LICENSE create mode 100644 backend/node_modules/@auth0/auth0-vue/README.md create mode 100644 backend/node_modules/@auth0/auth0-vue/package.json create mode 100644 backend/node_modules/@auth0/auth0-vue/src/global.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/guard.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/index.cjs.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/index.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/interfaces/app-state.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-plugin-options.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client-options.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/interfaces/index.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/plugin.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/token.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/utils.ts create mode 100644 backend/node_modules/@auth0/auth0-vue/src/version.ts create mode 100644 backend/node_modules/@babel/helper-string-parser/LICENSE create mode 100644 backend/node_modules/@babel/helper-string-parser/README.md create mode 100644 backend/node_modules/@babel/helper-string-parser/package.json create mode 100644 backend/node_modules/@babel/helper-validator-identifier/LICENSE create mode 100644 backend/node_modules/@babel/helper-validator-identifier/README.md create mode 100644 backend/node_modules/@babel/helper-validator-identifier/package.json create mode 100644 backend/node_modules/@babel/parser/CHANGELOG.md create mode 100644 backend/node_modules/@babel/parser/LICENSE create mode 100644 backend/node_modules/@babel/parser/README.md create mode 100755 backend/node_modules/@babel/parser/bin/babel-parser.js create mode 100644 backend/node_modules/@babel/parser/package.json create mode 100644 backend/node_modules/@babel/parser/typings/babel-parser.d.ts create mode 100644 backend/node_modules/@babel/types/LICENSE create mode 100644 backend/node_modules/@babel/types/README.md create mode 100644 backend/node_modules/@babel/types/package.json create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/LICENSE create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/README.md create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/package.json create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/src/strings.ts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts create mode 100644 backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map create mode 100644 backend/node_modules/@vue/compiler-core/LICENSE create mode 100644 backend/node_modules/@vue/compiler-core/README.md create mode 100644 backend/node_modules/@vue/compiler-core/index.js create mode 100644 backend/node_modules/@vue/compiler-core/package.json create mode 100644 backend/node_modules/@vue/compiler-dom/LICENSE create mode 100644 backend/node_modules/@vue/compiler-dom/README.md create mode 100644 backend/node_modules/@vue/compiler-dom/index.js create mode 100644 backend/node_modules/@vue/compiler-dom/package.json create mode 100644 backend/node_modules/@vue/compiler-sfc/LICENSE create mode 100644 backend/node_modules/@vue/compiler-sfc/README.md create mode 100644 backend/node_modules/@vue/compiler-sfc/package.json create mode 100644 backend/node_modules/@vue/compiler-ssr/LICENSE create mode 100644 backend/node_modules/@vue/compiler-ssr/README.md create mode 100644 backend/node_modules/@vue/compiler-ssr/package.json create mode 100644 backend/node_modules/@vue/reactivity/LICENSE create mode 100644 backend/node_modules/@vue/reactivity/README.md create mode 100644 backend/node_modules/@vue/reactivity/index.js create mode 100644 backend/node_modules/@vue/reactivity/package.json create mode 100644 backend/node_modules/@vue/runtime-core/LICENSE create mode 100644 backend/node_modules/@vue/runtime-core/README.md create mode 100644 backend/node_modules/@vue/runtime-core/index.js create mode 100644 backend/node_modules/@vue/runtime-core/package.json create mode 100644 backend/node_modules/@vue/runtime-dom/LICENSE create mode 100644 backend/node_modules/@vue/runtime-dom/README.md create mode 100644 backend/node_modules/@vue/runtime-dom/index.js create mode 100644 backend/node_modules/@vue/runtime-dom/package.json create mode 100644 backend/node_modules/@vue/server-renderer/LICENSE create mode 100644 backend/node_modules/@vue/server-renderer/README.md create mode 100644 backend/node_modules/@vue/server-renderer/index.js create mode 100644 backend/node_modules/@vue/server-renderer/package.json create mode 100644 backend/node_modules/@vue/shared/LICENSE create mode 100644 backend/node_modules/@vue/shared/README.md create mode 100644 backend/node_modules/@vue/shared/index.js create mode 100644 backend/node_modules/@vue/shared/package.json create mode 100644 backend/node_modules/browser-tabs-lock/CHANGELOG.md create mode 100644 backend/node_modules/browser-tabs-lock/LICENSE create mode 100644 backend/node_modules/browser-tabs-lock/README.md create mode 100644 backend/node_modules/browser-tabs-lock/bundleEntry.d.ts create mode 100644 backend/node_modules/browser-tabs-lock/bundleEntry.js create mode 100644 backend/node_modules/browser-tabs-lock/index.d.ts create mode 100644 backend/node_modules/browser-tabs-lock/index.js create mode 100644 backend/node_modules/browser-tabs-lock/package.json create mode 100644 backend/node_modules/browser-tabs-lock/processLock.d.ts create mode 100644 backend/node_modules/browser-tabs-lock/processLock.js create mode 100644 backend/node_modules/browser-tabs-lock/scripts/postinstall.js create mode 100644 backend/node_modules/csstype/LICENSE create mode 100644 backend/node_modules/csstype/README.md create mode 100644 backend/node_modules/csstype/index.d.ts create mode 100644 backend/node_modules/csstype/index.js.flow create mode 100644 backend/node_modules/csstype/package.json create mode 100644 backend/node_modules/dpop/LICENSE.md create mode 100644 backend/node_modules/dpop/README.md create mode 100644 backend/node_modules/dpop/package.json create mode 100644 backend/node_modules/entities/LICENSE create mode 100644 backend/node_modules/entities/decode.d.ts create mode 100644 backend/node_modules/entities/decode.js create mode 100644 backend/node_modules/entities/escape.d.ts create mode 100644 backend/node_modules/entities/escape.js create mode 100644 backend/node_modules/entities/package.json create mode 100644 backend/node_modules/entities/readme.md create mode 100644 backend/node_modules/entities/src/decode-codepoint.ts create mode 100644 backend/node_modules/entities/src/decode.ts create mode 100644 backend/node_modules/entities/src/encode.ts create mode 100644 backend/node_modules/entities/src/escape.ts create mode 100644 backend/node_modules/entities/src/generated/.eslintrc.json create mode 100644 backend/node_modules/entities/src/generated/decode-data-html.ts create mode 100644 backend/node_modules/entities/src/generated/decode-data-xml.ts create mode 100644 backend/node_modules/entities/src/generated/encode-html.ts create mode 100644 backend/node_modules/entities/src/index.ts create mode 100644 backend/node_modules/entities/src/internal/bin-trie-flags.ts create mode 100644 backend/node_modules/entities/src/internal/decode-shared.ts create mode 100644 backend/node_modules/entities/src/internal/encode-shared.ts create mode 100644 backend/node_modules/es-cookie/CHANGELOG.md create mode 100644 backend/node_modules/es-cookie/LICENSE.md create mode 100644 backend/node_modules/es-cookie/README.md create mode 100644 backend/node_modules/es-cookie/package.json create mode 100644 backend/node_modules/es-cookie/src/CookieAttributes.d.ts create mode 100644 backend/node_modules/es-cookie/src/CookieAttributes.js create mode 100644 backend/node_modules/es-cookie/src/es-cookie.d.ts create mode 100644 backend/node_modules/es-cookie/src/es-cookie.js create mode 100644 backend/node_modules/estree-walker/CHANGELOG.md create mode 100644 backend/node_modules/estree-walker/LICENSE create mode 100644 backend/node_modules/estree-walker/README.md create mode 100644 backend/node_modules/estree-walker/package.json create mode 100644 backend/node_modules/estree-walker/src/async.js create mode 100644 backend/node_modules/estree-walker/src/index.js create mode 100644 backend/node_modules/estree-walker/src/package.json create mode 100644 backend/node_modules/estree-walker/src/sync.js create mode 100644 backend/node_modules/estree-walker/src/walker.js create mode 100644 backend/node_modules/estree-walker/types/async.d.ts create mode 100644 backend/node_modules/estree-walker/types/index.d.ts create mode 100644 backend/node_modules/estree-walker/types/sync.d.ts create mode 100644 backend/node_modules/estree-walker/types/tsconfig.tsbuildinfo create mode 100644 backend/node_modules/estree-walker/types/walker.d.ts create mode 100644 backend/node_modules/jose/LICENSE.md create mode 100644 backend/node_modules/jose/README.md create mode 100644 backend/node_modules/jose/package.json create mode 100644 backend/node_modules/lodash/LICENSE create mode 100644 backend/node_modules/lodash/README.md create mode 100644 backend/node_modules/lodash/_DataView.js create mode 100644 backend/node_modules/lodash/_Hash.js create mode 100644 backend/node_modules/lodash/_LazyWrapper.js create mode 100644 backend/node_modules/lodash/_ListCache.js create mode 100644 backend/node_modules/lodash/_LodashWrapper.js create mode 100644 backend/node_modules/lodash/_Map.js create mode 100644 backend/node_modules/lodash/_MapCache.js create mode 100644 backend/node_modules/lodash/_Promise.js create mode 100644 backend/node_modules/lodash/_Set.js create mode 100644 backend/node_modules/lodash/_SetCache.js create mode 100644 backend/node_modules/lodash/_Stack.js create mode 100644 backend/node_modules/lodash/_Symbol.js create mode 100644 backend/node_modules/lodash/_Uint8Array.js create mode 100644 backend/node_modules/lodash/_WeakMap.js create mode 100644 backend/node_modules/lodash/_apply.js create mode 100644 backend/node_modules/lodash/_arrayAggregator.js create mode 100644 backend/node_modules/lodash/_arrayEach.js create mode 100644 backend/node_modules/lodash/_arrayEachRight.js create mode 100644 backend/node_modules/lodash/_arrayEvery.js create mode 100644 backend/node_modules/lodash/_arrayFilter.js create mode 100644 backend/node_modules/lodash/_arrayIncludes.js create mode 100644 backend/node_modules/lodash/_arrayIncludesWith.js create mode 100644 backend/node_modules/lodash/_arrayLikeKeys.js create mode 100644 backend/node_modules/lodash/_arrayMap.js create mode 100644 backend/node_modules/lodash/_arrayPush.js create mode 100644 backend/node_modules/lodash/_arrayReduce.js create mode 100644 backend/node_modules/lodash/_arrayReduceRight.js create mode 100644 backend/node_modules/lodash/_arraySample.js create mode 100644 backend/node_modules/lodash/_arraySampleSize.js create mode 100644 backend/node_modules/lodash/_arrayShuffle.js create mode 100644 backend/node_modules/lodash/_arraySome.js create mode 100644 backend/node_modules/lodash/_asciiSize.js create mode 100644 backend/node_modules/lodash/_asciiToArray.js create mode 100644 backend/node_modules/lodash/_asciiWords.js create mode 100644 backend/node_modules/lodash/_assignMergeValue.js create mode 100644 backend/node_modules/lodash/_assignValue.js create mode 100644 backend/node_modules/lodash/_assocIndexOf.js create mode 100644 backend/node_modules/lodash/_baseAggregator.js create mode 100644 backend/node_modules/lodash/_baseAssign.js create mode 100644 backend/node_modules/lodash/_baseAssignIn.js create mode 100644 backend/node_modules/lodash/_baseAssignValue.js create mode 100644 backend/node_modules/lodash/_baseAt.js create mode 100644 backend/node_modules/lodash/_baseClamp.js create mode 100644 backend/node_modules/lodash/_baseClone.js create mode 100644 backend/node_modules/lodash/_baseConforms.js create mode 100644 backend/node_modules/lodash/_baseConformsTo.js create mode 100644 backend/node_modules/lodash/_baseCreate.js create mode 100644 backend/node_modules/lodash/_baseDelay.js create mode 100644 backend/node_modules/lodash/_baseDifference.js create mode 100644 backend/node_modules/lodash/_baseEach.js create mode 100644 backend/node_modules/lodash/_baseEachRight.js create mode 100644 backend/node_modules/lodash/_baseEvery.js create mode 100644 backend/node_modules/lodash/_baseExtremum.js create mode 100644 backend/node_modules/lodash/_baseFill.js create mode 100644 backend/node_modules/lodash/_baseFilter.js create mode 100644 backend/node_modules/lodash/_baseFindIndex.js create mode 100644 backend/node_modules/lodash/_baseFindKey.js create mode 100644 backend/node_modules/lodash/_baseFlatten.js create mode 100644 backend/node_modules/lodash/_baseFor.js create mode 100644 backend/node_modules/lodash/_baseForOwn.js create mode 100644 backend/node_modules/lodash/_baseForOwnRight.js create mode 100644 backend/node_modules/lodash/_baseForRight.js create mode 100644 backend/node_modules/lodash/_baseFunctions.js create mode 100644 backend/node_modules/lodash/_baseGet.js create mode 100644 backend/node_modules/lodash/_baseGetAllKeys.js create mode 100644 backend/node_modules/lodash/_baseGetTag.js create mode 100644 backend/node_modules/lodash/_baseGt.js create mode 100644 backend/node_modules/lodash/_baseHas.js create mode 100644 backend/node_modules/lodash/_baseHasIn.js create mode 100644 backend/node_modules/lodash/_baseInRange.js create mode 100644 backend/node_modules/lodash/_baseIndexOf.js create mode 100644 backend/node_modules/lodash/_baseIndexOfWith.js create mode 100644 backend/node_modules/lodash/_baseIntersection.js create mode 100644 backend/node_modules/lodash/_baseInverter.js create mode 100644 backend/node_modules/lodash/_baseInvoke.js create mode 100644 backend/node_modules/lodash/_baseIsArguments.js create mode 100644 backend/node_modules/lodash/_baseIsArrayBuffer.js create mode 100644 backend/node_modules/lodash/_baseIsDate.js create mode 100644 backend/node_modules/lodash/_baseIsEqual.js create mode 100644 backend/node_modules/lodash/_baseIsEqualDeep.js create mode 100644 backend/node_modules/lodash/_baseIsMap.js create mode 100644 backend/node_modules/lodash/_baseIsMatch.js create mode 100644 backend/node_modules/lodash/_baseIsNaN.js create mode 100644 backend/node_modules/lodash/_baseIsNative.js create mode 100644 backend/node_modules/lodash/_baseIsRegExp.js create mode 100644 backend/node_modules/lodash/_baseIsSet.js create mode 100644 backend/node_modules/lodash/_baseIsTypedArray.js create mode 100644 backend/node_modules/lodash/_baseIteratee.js create mode 100644 backend/node_modules/lodash/_baseKeys.js create mode 100644 backend/node_modules/lodash/_baseKeysIn.js create mode 100644 backend/node_modules/lodash/_baseLodash.js create mode 100644 backend/node_modules/lodash/_baseLt.js create mode 100644 backend/node_modules/lodash/_baseMap.js create mode 100644 backend/node_modules/lodash/_baseMatches.js create mode 100644 backend/node_modules/lodash/_baseMatchesProperty.js create mode 100644 backend/node_modules/lodash/_baseMean.js create mode 100644 backend/node_modules/lodash/_baseMerge.js create mode 100644 backend/node_modules/lodash/_baseMergeDeep.js create mode 100644 backend/node_modules/lodash/_baseNth.js create mode 100644 backend/node_modules/lodash/_baseOrderBy.js create mode 100644 backend/node_modules/lodash/_basePick.js create mode 100644 backend/node_modules/lodash/_basePickBy.js create mode 100644 backend/node_modules/lodash/_baseProperty.js create mode 100644 backend/node_modules/lodash/_basePropertyDeep.js create mode 100644 backend/node_modules/lodash/_basePropertyOf.js create mode 100644 backend/node_modules/lodash/_basePullAll.js create mode 100644 backend/node_modules/lodash/_basePullAt.js create mode 100644 backend/node_modules/lodash/_baseRandom.js create mode 100644 backend/node_modules/lodash/_baseRange.js create mode 100644 backend/node_modules/lodash/_baseReduce.js create mode 100644 backend/node_modules/lodash/_baseRepeat.js create mode 100644 backend/node_modules/lodash/_baseRest.js create mode 100644 backend/node_modules/lodash/_baseSample.js create mode 100644 backend/node_modules/lodash/_baseSampleSize.js create mode 100644 backend/node_modules/lodash/_baseSet.js create mode 100644 backend/node_modules/lodash/_baseSetData.js create mode 100644 backend/node_modules/lodash/_baseSetToString.js create mode 100644 backend/node_modules/lodash/_baseShuffle.js create mode 100644 backend/node_modules/lodash/_baseSlice.js create mode 100644 backend/node_modules/lodash/_baseSome.js create mode 100644 backend/node_modules/lodash/_baseSortBy.js create mode 100644 backend/node_modules/lodash/_baseSortedIndex.js create mode 100644 backend/node_modules/lodash/_baseSortedIndexBy.js create mode 100644 backend/node_modules/lodash/_baseSortedUniq.js create mode 100644 backend/node_modules/lodash/_baseSum.js create mode 100644 backend/node_modules/lodash/_baseTimes.js create mode 100644 backend/node_modules/lodash/_baseToNumber.js create mode 100644 backend/node_modules/lodash/_baseToPairs.js create mode 100644 backend/node_modules/lodash/_baseToString.js create mode 100644 backend/node_modules/lodash/_baseTrim.js create mode 100644 backend/node_modules/lodash/_baseUnary.js create mode 100644 backend/node_modules/lodash/_baseUniq.js create mode 100644 backend/node_modules/lodash/_baseUnset.js create mode 100644 backend/node_modules/lodash/_baseUpdate.js create mode 100644 backend/node_modules/lodash/_baseValues.js create mode 100644 backend/node_modules/lodash/_baseWhile.js create mode 100644 backend/node_modules/lodash/_baseWrapperValue.js create mode 100644 backend/node_modules/lodash/_baseXor.js create mode 100644 backend/node_modules/lodash/_baseZipObject.js create mode 100644 backend/node_modules/lodash/_cacheHas.js create mode 100644 backend/node_modules/lodash/_castArrayLikeObject.js create mode 100644 backend/node_modules/lodash/_castFunction.js create mode 100644 backend/node_modules/lodash/_castPath.js create mode 100644 backend/node_modules/lodash/_castRest.js create mode 100644 backend/node_modules/lodash/_castSlice.js create mode 100644 backend/node_modules/lodash/_charsEndIndex.js create mode 100644 backend/node_modules/lodash/_charsStartIndex.js create mode 100644 backend/node_modules/lodash/_cloneArrayBuffer.js create mode 100644 backend/node_modules/lodash/_cloneBuffer.js create mode 100644 backend/node_modules/lodash/_cloneDataView.js create mode 100644 backend/node_modules/lodash/_cloneRegExp.js create mode 100644 backend/node_modules/lodash/_cloneSymbol.js create mode 100644 backend/node_modules/lodash/_cloneTypedArray.js create mode 100644 backend/node_modules/lodash/_compareAscending.js create mode 100644 backend/node_modules/lodash/_compareMultiple.js create mode 100644 backend/node_modules/lodash/_composeArgs.js create mode 100644 backend/node_modules/lodash/_composeArgsRight.js create mode 100644 backend/node_modules/lodash/_copyArray.js create mode 100644 backend/node_modules/lodash/_copyObject.js create mode 100644 backend/node_modules/lodash/_copySymbols.js create mode 100644 backend/node_modules/lodash/_copySymbolsIn.js create mode 100644 backend/node_modules/lodash/_coreJsData.js create mode 100644 backend/node_modules/lodash/_countHolders.js create mode 100644 backend/node_modules/lodash/_createAggregator.js create mode 100644 backend/node_modules/lodash/_createAssigner.js create mode 100644 backend/node_modules/lodash/_createBaseEach.js create mode 100644 backend/node_modules/lodash/_createBaseFor.js create mode 100644 backend/node_modules/lodash/_createBind.js create mode 100644 backend/node_modules/lodash/_createCaseFirst.js create mode 100644 backend/node_modules/lodash/_createCompounder.js create mode 100644 backend/node_modules/lodash/_createCtor.js create mode 100644 backend/node_modules/lodash/_createCurry.js create mode 100644 backend/node_modules/lodash/_createFind.js create mode 100644 backend/node_modules/lodash/_createFlow.js create mode 100644 backend/node_modules/lodash/_createHybrid.js create mode 100644 backend/node_modules/lodash/_createInverter.js create mode 100644 backend/node_modules/lodash/_createMathOperation.js create mode 100644 backend/node_modules/lodash/_createOver.js create mode 100644 backend/node_modules/lodash/_createPadding.js create mode 100644 backend/node_modules/lodash/_createPartial.js create mode 100644 backend/node_modules/lodash/_createRange.js create mode 100644 backend/node_modules/lodash/_createRecurry.js create mode 100644 backend/node_modules/lodash/_createRelationalOperation.js create mode 100644 backend/node_modules/lodash/_createRound.js create mode 100644 backend/node_modules/lodash/_createSet.js create mode 100644 backend/node_modules/lodash/_createToPairs.js create mode 100644 backend/node_modules/lodash/_createWrap.js create mode 100644 backend/node_modules/lodash/_customDefaultsAssignIn.js create mode 100644 backend/node_modules/lodash/_customDefaultsMerge.js create mode 100644 backend/node_modules/lodash/_customOmitClone.js create mode 100644 backend/node_modules/lodash/_deburrLetter.js create mode 100644 backend/node_modules/lodash/_defineProperty.js create mode 100644 backend/node_modules/lodash/_equalArrays.js create mode 100644 backend/node_modules/lodash/_equalByTag.js create mode 100644 backend/node_modules/lodash/_equalObjects.js create mode 100644 backend/node_modules/lodash/_escapeHtmlChar.js create mode 100644 backend/node_modules/lodash/_escapeStringChar.js create mode 100644 backend/node_modules/lodash/_flatRest.js create mode 100644 backend/node_modules/lodash/_freeGlobal.js create mode 100644 backend/node_modules/lodash/_getAllKeys.js create mode 100644 backend/node_modules/lodash/_getAllKeysIn.js create mode 100644 backend/node_modules/lodash/_getData.js create mode 100644 backend/node_modules/lodash/_getFuncName.js create mode 100644 backend/node_modules/lodash/_getHolder.js create mode 100644 backend/node_modules/lodash/_getMapData.js create mode 100644 backend/node_modules/lodash/_getMatchData.js create mode 100644 backend/node_modules/lodash/_getNative.js create mode 100644 backend/node_modules/lodash/_getPrototype.js create mode 100644 backend/node_modules/lodash/_getRawTag.js create mode 100644 backend/node_modules/lodash/_getSymbols.js create mode 100644 backend/node_modules/lodash/_getSymbolsIn.js create mode 100644 backend/node_modules/lodash/_getTag.js create mode 100644 backend/node_modules/lodash/_getValue.js create mode 100644 backend/node_modules/lodash/_getView.js create mode 100644 backend/node_modules/lodash/_getWrapDetails.js create mode 100644 backend/node_modules/lodash/_hasPath.js create mode 100644 backend/node_modules/lodash/_hasUnicode.js create mode 100644 backend/node_modules/lodash/_hasUnicodeWord.js create mode 100644 backend/node_modules/lodash/_hashClear.js create mode 100644 backend/node_modules/lodash/_hashDelete.js create mode 100644 backend/node_modules/lodash/_hashGet.js create mode 100644 backend/node_modules/lodash/_hashHas.js create mode 100644 backend/node_modules/lodash/_hashSet.js create mode 100644 backend/node_modules/lodash/_initCloneArray.js create mode 100644 backend/node_modules/lodash/_initCloneByTag.js create mode 100644 backend/node_modules/lodash/_initCloneObject.js create mode 100644 backend/node_modules/lodash/_insertWrapDetails.js create mode 100644 backend/node_modules/lodash/_isFlattenable.js create mode 100644 backend/node_modules/lodash/_isIndex.js create mode 100644 backend/node_modules/lodash/_isIterateeCall.js create mode 100644 backend/node_modules/lodash/_isKey.js create mode 100644 backend/node_modules/lodash/_isKeyable.js create mode 100644 backend/node_modules/lodash/_isLaziable.js create mode 100644 backend/node_modules/lodash/_isMaskable.js create mode 100644 backend/node_modules/lodash/_isMasked.js create mode 100644 backend/node_modules/lodash/_isPrototype.js create mode 100644 backend/node_modules/lodash/_isStrictComparable.js create mode 100644 backend/node_modules/lodash/_iteratorToArray.js create mode 100644 backend/node_modules/lodash/_lazyClone.js create mode 100644 backend/node_modules/lodash/_lazyReverse.js create mode 100644 backend/node_modules/lodash/_lazyValue.js create mode 100644 backend/node_modules/lodash/_listCacheClear.js create mode 100644 backend/node_modules/lodash/_listCacheDelete.js create mode 100644 backend/node_modules/lodash/_listCacheGet.js create mode 100644 backend/node_modules/lodash/_listCacheHas.js create mode 100644 backend/node_modules/lodash/_listCacheSet.js create mode 100644 backend/node_modules/lodash/_mapCacheClear.js create mode 100644 backend/node_modules/lodash/_mapCacheDelete.js create mode 100644 backend/node_modules/lodash/_mapCacheGet.js create mode 100644 backend/node_modules/lodash/_mapCacheHas.js create mode 100644 backend/node_modules/lodash/_mapCacheSet.js create mode 100644 backend/node_modules/lodash/_mapToArray.js create mode 100644 backend/node_modules/lodash/_matchesStrictComparable.js create mode 100644 backend/node_modules/lodash/_memoizeCapped.js create mode 100644 backend/node_modules/lodash/_mergeData.js create mode 100644 backend/node_modules/lodash/_metaMap.js create mode 100644 backend/node_modules/lodash/_nativeCreate.js create mode 100644 backend/node_modules/lodash/_nativeKeys.js create mode 100644 backend/node_modules/lodash/_nativeKeysIn.js create mode 100644 backend/node_modules/lodash/_nodeUtil.js create mode 100644 backend/node_modules/lodash/_objectToString.js create mode 100644 backend/node_modules/lodash/_overArg.js create mode 100644 backend/node_modules/lodash/_overRest.js create mode 100644 backend/node_modules/lodash/_parent.js create mode 100644 backend/node_modules/lodash/_reEscape.js create mode 100644 backend/node_modules/lodash/_reEvaluate.js create mode 100644 backend/node_modules/lodash/_reInterpolate.js create mode 100644 backend/node_modules/lodash/_realNames.js create mode 100644 backend/node_modules/lodash/_reorder.js create mode 100644 backend/node_modules/lodash/_replaceHolders.js create mode 100644 backend/node_modules/lodash/_root.js create mode 100644 backend/node_modules/lodash/_safeGet.js create mode 100644 backend/node_modules/lodash/_setCacheAdd.js create mode 100644 backend/node_modules/lodash/_setCacheHas.js create mode 100644 backend/node_modules/lodash/_setData.js create mode 100644 backend/node_modules/lodash/_setToArray.js create mode 100644 backend/node_modules/lodash/_setToPairs.js create mode 100644 backend/node_modules/lodash/_setToString.js create mode 100644 backend/node_modules/lodash/_setWrapToString.js create mode 100644 backend/node_modules/lodash/_shortOut.js create mode 100644 backend/node_modules/lodash/_shuffleSelf.js create mode 100644 backend/node_modules/lodash/_stackClear.js create mode 100644 backend/node_modules/lodash/_stackDelete.js create mode 100644 backend/node_modules/lodash/_stackGet.js create mode 100644 backend/node_modules/lodash/_stackHas.js create mode 100644 backend/node_modules/lodash/_stackSet.js create mode 100644 backend/node_modules/lodash/_strictIndexOf.js create mode 100644 backend/node_modules/lodash/_strictLastIndexOf.js create mode 100644 backend/node_modules/lodash/_stringSize.js create mode 100644 backend/node_modules/lodash/_stringToArray.js create mode 100644 backend/node_modules/lodash/_stringToPath.js create mode 100644 backend/node_modules/lodash/_toKey.js create mode 100644 backend/node_modules/lodash/_toSource.js create mode 100644 backend/node_modules/lodash/_trimmedEndIndex.js create mode 100644 backend/node_modules/lodash/_unescapeHtmlChar.js create mode 100644 backend/node_modules/lodash/_unicodeSize.js create mode 100644 backend/node_modules/lodash/_unicodeToArray.js create mode 100644 backend/node_modules/lodash/_unicodeWords.js create mode 100644 backend/node_modules/lodash/_updateWrapDetails.js create mode 100644 backend/node_modules/lodash/_wrapperClone.js create mode 100644 backend/node_modules/lodash/add.js create mode 100644 backend/node_modules/lodash/after.js create mode 100644 backend/node_modules/lodash/array.js create mode 100644 backend/node_modules/lodash/ary.js create mode 100644 backend/node_modules/lodash/assign.js create mode 100644 backend/node_modules/lodash/assignIn.js create mode 100644 backend/node_modules/lodash/assignInWith.js create mode 100644 backend/node_modules/lodash/assignWith.js create mode 100644 backend/node_modules/lodash/at.js create mode 100644 backend/node_modules/lodash/attempt.js create mode 100644 backend/node_modules/lodash/before.js create mode 100644 backend/node_modules/lodash/bind.js create mode 100644 backend/node_modules/lodash/bindAll.js create mode 100644 backend/node_modules/lodash/bindKey.js create mode 100644 backend/node_modules/lodash/camelCase.js create mode 100644 backend/node_modules/lodash/capitalize.js create mode 100644 backend/node_modules/lodash/castArray.js create mode 100644 backend/node_modules/lodash/ceil.js create mode 100644 backend/node_modules/lodash/chain.js create mode 100644 backend/node_modules/lodash/chunk.js create mode 100644 backend/node_modules/lodash/clamp.js create mode 100644 backend/node_modules/lodash/clone.js create mode 100644 backend/node_modules/lodash/cloneDeep.js create mode 100644 backend/node_modules/lodash/cloneDeepWith.js create mode 100644 backend/node_modules/lodash/cloneWith.js create mode 100644 backend/node_modules/lodash/collection.js create mode 100644 backend/node_modules/lodash/commit.js create mode 100644 backend/node_modules/lodash/compact.js create mode 100644 backend/node_modules/lodash/concat.js create mode 100644 backend/node_modules/lodash/cond.js create mode 100644 backend/node_modules/lodash/conforms.js create mode 100644 backend/node_modules/lodash/conformsTo.js create mode 100644 backend/node_modules/lodash/constant.js create mode 100644 backend/node_modules/lodash/core.js create mode 100644 backend/node_modules/lodash/core.min.js create mode 100644 backend/node_modules/lodash/countBy.js create mode 100644 backend/node_modules/lodash/create.js create mode 100644 backend/node_modules/lodash/curry.js create mode 100644 backend/node_modules/lodash/curryRight.js create mode 100644 backend/node_modules/lodash/date.js create mode 100644 backend/node_modules/lodash/debounce.js create mode 100644 backend/node_modules/lodash/deburr.js create mode 100644 backend/node_modules/lodash/defaultTo.js create mode 100644 backend/node_modules/lodash/defaults.js create mode 100644 backend/node_modules/lodash/defaultsDeep.js create mode 100644 backend/node_modules/lodash/defer.js create mode 100644 backend/node_modules/lodash/delay.js create mode 100644 backend/node_modules/lodash/difference.js create mode 100644 backend/node_modules/lodash/differenceBy.js create mode 100644 backend/node_modules/lodash/differenceWith.js create mode 100644 backend/node_modules/lodash/divide.js create mode 100644 backend/node_modules/lodash/drop.js create mode 100644 backend/node_modules/lodash/dropRight.js create mode 100644 backend/node_modules/lodash/dropRightWhile.js create mode 100644 backend/node_modules/lodash/dropWhile.js create mode 100644 backend/node_modules/lodash/each.js create mode 100644 backend/node_modules/lodash/eachRight.js create mode 100644 backend/node_modules/lodash/endsWith.js create mode 100644 backend/node_modules/lodash/entries.js create mode 100644 backend/node_modules/lodash/entriesIn.js create mode 100644 backend/node_modules/lodash/eq.js create mode 100644 backend/node_modules/lodash/escape.js create mode 100644 backend/node_modules/lodash/escapeRegExp.js create mode 100644 backend/node_modules/lodash/every.js create mode 100644 backend/node_modules/lodash/extend.js create mode 100644 backend/node_modules/lodash/extendWith.js create mode 100644 backend/node_modules/lodash/fill.js create mode 100644 backend/node_modules/lodash/filter.js create mode 100644 backend/node_modules/lodash/find.js create mode 100644 backend/node_modules/lodash/findIndex.js create mode 100644 backend/node_modules/lodash/findKey.js create mode 100644 backend/node_modules/lodash/findLast.js create mode 100644 backend/node_modules/lodash/findLastIndex.js create mode 100644 backend/node_modules/lodash/findLastKey.js create mode 100644 backend/node_modules/lodash/first.js create mode 100644 backend/node_modules/lodash/flatMap.js create mode 100644 backend/node_modules/lodash/flatMapDeep.js create mode 100644 backend/node_modules/lodash/flatMapDepth.js create mode 100644 backend/node_modules/lodash/flatten.js create mode 100644 backend/node_modules/lodash/flattenDeep.js create mode 100644 backend/node_modules/lodash/flattenDepth.js create mode 100644 backend/node_modules/lodash/flip.js create mode 100644 backend/node_modules/lodash/floor.js create mode 100644 backend/node_modules/lodash/flow.js create mode 100644 backend/node_modules/lodash/flowRight.js create mode 100644 backend/node_modules/lodash/forEach.js create mode 100644 backend/node_modules/lodash/forEachRight.js create mode 100644 backend/node_modules/lodash/forIn.js create mode 100644 backend/node_modules/lodash/forInRight.js create mode 100644 backend/node_modules/lodash/forOwn.js create mode 100644 backend/node_modules/lodash/forOwnRight.js create mode 100644 backend/node_modules/lodash/fp.js create mode 100644 backend/node_modules/lodash/fp/F.js create mode 100644 backend/node_modules/lodash/fp/T.js create mode 100644 backend/node_modules/lodash/fp/__.js create mode 100644 backend/node_modules/lodash/fp/_baseConvert.js create mode 100644 backend/node_modules/lodash/fp/_convertBrowser.js create mode 100644 backend/node_modules/lodash/fp/_falseOptions.js create mode 100644 backend/node_modules/lodash/fp/_mapping.js create mode 100644 backend/node_modules/lodash/fp/_util.js create mode 100644 backend/node_modules/lodash/fp/add.js create mode 100644 backend/node_modules/lodash/fp/after.js create mode 100644 backend/node_modules/lodash/fp/all.js create mode 100644 backend/node_modules/lodash/fp/allPass.js create mode 100644 backend/node_modules/lodash/fp/always.js create mode 100644 backend/node_modules/lodash/fp/any.js create mode 100644 backend/node_modules/lodash/fp/anyPass.js create mode 100644 backend/node_modules/lodash/fp/apply.js create mode 100644 backend/node_modules/lodash/fp/array.js create mode 100644 backend/node_modules/lodash/fp/ary.js create mode 100644 backend/node_modules/lodash/fp/assign.js create mode 100644 backend/node_modules/lodash/fp/assignAll.js create mode 100644 backend/node_modules/lodash/fp/assignAllWith.js create mode 100644 backend/node_modules/lodash/fp/assignIn.js create mode 100644 backend/node_modules/lodash/fp/assignInAll.js create mode 100644 backend/node_modules/lodash/fp/assignInAllWith.js create mode 100644 backend/node_modules/lodash/fp/assignInWith.js create mode 100644 backend/node_modules/lodash/fp/assignWith.js create mode 100644 backend/node_modules/lodash/fp/assoc.js create mode 100644 backend/node_modules/lodash/fp/assocPath.js create mode 100644 backend/node_modules/lodash/fp/at.js create mode 100644 backend/node_modules/lodash/fp/attempt.js create mode 100644 backend/node_modules/lodash/fp/before.js create mode 100644 backend/node_modules/lodash/fp/bind.js create mode 100644 backend/node_modules/lodash/fp/bindAll.js create mode 100644 backend/node_modules/lodash/fp/bindKey.js create mode 100644 backend/node_modules/lodash/fp/camelCase.js create mode 100644 backend/node_modules/lodash/fp/capitalize.js create mode 100644 backend/node_modules/lodash/fp/castArray.js create mode 100644 backend/node_modules/lodash/fp/ceil.js create mode 100644 backend/node_modules/lodash/fp/chain.js create mode 100644 backend/node_modules/lodash/fp/chunk.js create mode 100644 backend/node_modules/lodash/fp/clamp.js create mode 100644 backend/node_modules/lodash/fp/clone.js create mode 100644 backend/node_modules/lodash/fp/cloneDeep.js create mode 100644 backend/node_modules/lodash/fp/cloneDeepWith.js create mode 100644 backend/node_modules/lodash/fp/cloneWith.js create mode 100644 backend/node_modules/lodash/fp/collection.js create mode 100644 backend/node_modules/lodash/fp/commit.js create mode 100644 backend/node_modules/lodash/fp/compact.js create mode 100644 backend/node_modules/lodash/fp/complement.js create mode 100644 backend/node_modules/lodash/fp/compose.js create mode 100644 backend/node_modules/lodash/fp/concat.js create mode 100644 backend/node_modules/lodash/fp/cond.js create mode 100644 backend/node_modules/lodash/fp/conforms.js create mode 100644 backend/node_modules/lodash/fp/conformsTo.js create mode 100644 backend/node_modules/lodash/fp/constant.js create mode 100644 backend/node_modules/lodash/fp/contains.js create mode 100644 backend/node_modules/lodash/fp/convert.js create mode 100644 backend/node_modules/lodash/fp/countBy.js create mode 100644 backend/node_modules/lodash/fp/create.js create mode 100644 backend/node_modules/lodash/fp/curry.js create mode 100644 backend/node_modules/lodash/fp/curryN.js create mode 100644 backend/node_modules/lodash/fp/curryRight.js create mode 100644 backend/node_modules/lodash/fp/curryRightN.js create mode 100644 backend/node_modules/lodash/fp/date.js create mode 100644 backend/node_modules/lodash/fp/debounce.js create mode 100644 backend/node_modules/lodash/fp/deburr.js create mode 100644 backend/node_modules/lodash/fp/defaultTo.js create mode 100644 backend/node_modules/lodash/fp/defaults.js create mode 100644 backend/node_modules/lodash/fp/defaultsAll.js create mode 100644 backend/node_modules/lodash/fp/defaultsDeep.js create mode 100644 backend/node_modules/lodash/fp/defaultsDeepAll.js create mode 100644 backend/node_modules/lodash/fp/defer.js create mode 100644 backend/node_modules/lodash/fp/delay.js create mode 100644 backend/node_modules/lodash/fp/difference.js create mode 100644 backend/node_modules/lodash/fp/differenceBy.js create mode 100644 backend/node_modules/lodash/fp/differenceWith.js create mode 100644 backend/node_modules/lodash/fp/dissoc.js create mode 100644 backend/node_modules/lodash/fp/dissocPath.js create mode 100644 backend/node_modules/lodash/fp/divide.js create mode 100644 backend/node_modules/lodash/fp/drop.js create mode 100644 backend/node_modules/lodash/fp/dropLast.js create mode 100644 backend/node_modules/lodash/fp/dropLastWhile.js create mode 100644 backend/node_modules/lodash/fp/dropRight.js create mode 100644 backend/node_modules/lodash/fp/dropRightWhile.js create mode 100644 backend/node_modules/lodash/fp/dropWhile.js create mode 100644 backend/node_modules/lodash/fp/each.js create mode 100644 backend/node_modules/lodash/fp/eachRight.js create mode 100644 backend/node_modules/lodash/fp/endsWith.js create mode 100644 backend/node_modules/lodash/fp/entries.js create mode 100644 backend/node_modules/lodash/fp/entriesIn.js create mode 100644 backend/node_modules/lodash/fp/eq.js create mode 100644 backend/node_modules/lodash/fp/equals.js create mode 100644 backend/node_modules/lodash/fp/escape.js create mode 100644 backend/node_modules/lodash/fp/escapeRegExp.js create mode 100644 backend/node_modules/lodash/fp/every.js create mode 100644 backend/node_modules/lodash/fp/extend.js create mode 100644 backend/node_modules/lodash/fp/extendAll.js create mode 100644 backend/node_modules/lodash/fp/extendAllWith.js create mode 100644 backend/node_modules/lodash/fp/extendWith.js create mode 100644 backend/node_modules/lodash/fp/fill.js create mode 100644 backend/node_modules/lodash/fp/filter.js create mode 100644 backend/node_modules/lodash/fp/find.js create mode 100644 backend/node_modules/lodash/fp/findFrom.js create mode 100644 backend/node_modules/lodash/fp/findIndex.js create mode 100644 backend/node_modules/lodash/fp/findIndexFrom.js create mode 100644 backend/node_modules/lodash/fp/findKey.js create mode 100644 backend/node_modules/lodash/fp/findLast.js create mode 100644 backend/node_modules/lodash/fp/findLastFrom.js create mode 100644 backend/node_modules/lodash/fp/findLastIndex.js create mode 100644 backend/node_modules/lodash/fp/findLastIndexFrom.js create mode 100644 backend/node_modules/lodash/fp/findLastKey.js create mode 100644 backend/node_modules/lodash/fp/first.js create mode 100644 backend/node_modules/lodash/fp/flatMap.js create mode 100644 backend/node_modules/lodash/fp/flatMapDeep.js create mode 100644 backend/node_modules/lodash/fp/flatMapDepth.js create mode 100644 backend/node_modules/lodash/fp/flatten.js create mode 100644 backend/node_modules/lodash/fp/flattenDeep.js create mode 100644 backend/node_modules/lodash/fp/flattenDepth.js create mode 100644 backend/node_modules/lodash/fp/flip.js create mode 100644 backend/node_modules/lodash/fp/floor.js create mode 100644 backend/node_modules/lodash/fp/flow.js create mode 100644 backend/node_modules/lodash/fp/flowRight.js create mode 100644 backend/node_modules/lodash/fp/forEach.js create mode 100644 backend/node_modules/lodash/fp/forEachRight.js create mode 100644 backend/node_modules/lodash/fp/forIn.js create mode 100644 backend/node_modules/lodash/fp/forInRight.js create mode 100644 backend/node_modules/lodash/fp/forOwn.js create mode 100644 backend/node_modules/lodash/fp/forOwnRight.js create mode 100644 backend/node_modules/lodash/fp/fromPairs.js create mode 100644 backend/node_modules/lodash/fp/function.js create mode 100644 backend/node_modules/lodash/fp/functions.js create mode 100644 backend/node_modules/lodash/fp/functionsIn.js create mode 100644 backend/node_modules/lodash/fp/get.js create mode 100644 backend/node_modules/lodash/fp/getOr.js create mode 100644 backend/node_modules/lodash/fp/groupBy.js create mode 100644 backend/node_modules/lodash/fp/gt.js create mode 100644 backend/node_modules/lodash/fp/gte.js create mode 100644 backend/node_modules/lodash/fp/has.js create mode 100644 backend/node_modules/lodash/fp/hasIn.js create mode 100644 backend/node_modules/lodash/fp/head.js create mode 100644 backend/node_modules/lodash/fp/identical.js create mode 100644 backend/node_modules/lodash/fp/identity.js create mode 100644 backend/node_modules/lodash/fp/inRange.js create mode 100644 backend/node_modules/lodash/fp/includes.js create mode 100644 backend/node_modules/lodash/fp/includesFrom.js create mode 100644 backend/node_modules/lodash/fp/indexBy.js create mode 100644 backend/node_modules/lodash/fp/indexOf.js create mode 100644 backend/node_modules/lodash/fp/indexOfFrom.js create mode 100644 backend/node_modules/lodash/fp/init.js create mode 100644 backend/node_modules/lodash/fp/initial.js create mode 100644 backend/node_modules/lodash/fp/intersection.js create mode 100644 backend/node_modules/lodash/fp/intersectionBy.js create mode 100644 backend/node_modules/lodash/fp/intersectionWith.js create mode 100644 backend/node_modules/lodash/fp/invert.js create mode 100644 backend/node_modules/lodash/fp/invertBy.js create mode 100644 backend/node_modules/lodash/fp/invertObj.js create mode 100644 backend/node_modules/lodash/fp/invoke.js create mode 100644 backend/node_modules/lodash/fp/invokeArgs.js create mode 100644 backend/node_modules/lodash/fp/invokeArgsMap.js create mode 100644 backend/node_modules/lodash/fp/invokeMap.js create mode 100644 backend/node_modules/lodash/fp/isArguments.js create mode 100644 backend/node_modules/lodash/fp/isArray.js create mode 100644 backend/node_modules/lodash/fp/isArrayBuffer.js create mode 100644 backend/node_modules/lodash/fp/isArrayLike.js create mode 100644 backend/node_modules/lodash/fp/isArrayLikeObject.js create mode 100644 backend/node_modules/lodash/fp/isBoolean.js create mode 100644 backend/node_modules/lodash/fp/isBuffer.js create mode 100644 backend/node_modules/lodash/fp/isDate.js create mode 100644 backend/node_modules/lodash/fp/isElement.js create mode 100644 backend/node_modules/lodash/fp/isEmpty.js create mode 100644 backend/node_modules/lodash/fp/isEqual.js create mode 100644 backend/node_modules/lodash/fp/isEqualWith.js create mode 100644 backend/node_modules/lodash/fp/isError.js create mode 100644 backend/node_modules/lodash/fp/isFinite.js create mode 100644 backend/node_modules/lodash/fp/isFunction.js create mode 100644 backend/node_modules/lodash/fp/isInteger.js create mode 100644 backend/node_modules/lodash/fp/isLength.js create mode 100644 backend/node_modules/lodash/fp/isMap.js create mode 100644 backend/node_modules/lodash/fp/isMatch.js create mode 100644 backend/node_modules/lodash/fp/isMatchWith.js create mode 100644 backend/node_modules/lodash/fp/isNaN.js create mode 100644 backend/node_modules/lodash/fp/isNative.js create mode 100644 backend/node_modules/lodash/fp/isNil.js create mode 100644 backend/node_modules/lodash/fp/isNull.js create mode 100644 backend/node_modules/lodash/fp/isNumber.js create mode 100644 backend/node_modules/lodash/fp/isObject.js create mode 100644 backend/node_modules/lodash/fp/isObjectLike.js create mode 100644 backend/node_modules/lodash/fp/isPlainObject.js create mode 100644 backend/node_modules/lodash/fp/isRegExp.js create mode 100644 backend/node_modules/lodash/fp/isSafeInteger.js create mode 100644 backend/node_modules/lodash/fp/isSet.js create mode 100644 backend/node_modules/lodash/fp/isString.js create mode 100644 backend/node_modules/lodash/fp/isSymbol.js create mode 100644 backend/node_modules/lodash/fp/isTypedArray.js create mode 100644 backend/node_modules/lodash/fp/isUndefined.js create mode 100644 backend/node_modules/lodash/fp/isWeakMap.js create mode 100644 backend/node_modules/lodash/fp/isWeakSet.js create mode 100644 backend/node_modules/lodash/fp/iteratee.js create mode 100644 backend/node_modules/lodash/fp/join.js create mode 100644 backend/node_modules/lodash/fp/juxt.js create mode 100644 backend/node_modules/lodash/fp/kebabCase.js create mode 100644 backend/node_modules/lodash/fp/keyBy.js create mode 100644 backend/node_modules/lodash/fp/keys.js create mode 100644 backend/node_modules/lodash/fp/keysIn.js create mode 100644 backend/node_modules/lodash/fp/lang.js create mode 100644 backend/node_modules/lodash/fp/last.js create mode 100644 backend/node_modules/lodash/fp/lastIndexOf.js create mode 100644 backend/node_modules/lodash/fp/lastIndexOfFrom.js create mode 100644 backend/node_modules/lodash/fp/lowerCase.js create mode 100644 backend/node_modules/lodash/fp/lowerFirst.js create mode 100644 backend/node_modules/lodash/fp/lt.js create mode 100644 backend/node_modules/lodash/fp/lte.js create mode 100644 backend/node_modules/lodash/fp/map.js create mode 100644 backend/node_modules/lodash/fp/mapKeys.js create mode 100644 backend/node_modules/lodash/fp/mapValues.js create mode 100644 backend/node_modules/lodash/fp/matches.js create mode 100644 backend/node_modules/lodash/fp/matchesProperty.js create mode 100644 backend/node_modules/lodash/fp/math.js create mode 100644 backend/node_modules/lodash/fp/max.js create mode 100644 backend/node_modules/lodash/fp/maxBy.js create mode 100644 backend/node_modules/lodash/fp/mean.js create mode 100644 backend/node_modules/lodash/fp/meanBy.js create mode 100644 backend/node_modules/lodash/fp/memoize.js create mode 100644 backend/node_modules/lodash/fp/merge.js create mode 100644 backend/node_modules/lodash/fp/mergeAll.js create mode 100644 backend/node_modules/lodash/fp/mergeAllWith.js create mode 100644 backend/node_modules/lodash/fp/mergeWith.js create mode 100644 backend/node_modules/lodash/fp/method.js create mode 100644 backend/node_modules/lodash/fp/methodOf.js create mode 100644 backend/node_modules/lodash/fp/min.js create mode 100644 backend/node_modules/lodash/fp/minBy.js create mode 100644 backend/node_modules/lodash/fp/mixin.js create mode 100644 backend/node_modules/lodash/fp/multiply.js create mode 100644 backend/node_modules/lodash/fp/nAry.js create mode 100644 backend/node_modules/lodash/fp/negate.js create mode 100644 backend/node_modules/lodash/fp/next.js create mode 100644 backend/node_modules/lodash/fp/noop.js create mode 100644 backend/node_modules/lodash/fp/now.js create mode 100644 backend/node_modules/lodash/fp/nth.js create mode 100644 backend/node_modules/lodash/fp/nthArg.js create mode 100644 backend/node_modules/lodash/fp/number.js create mode 100644 backend/node_modules/lodash/fp/object.js create mode 100644 backend/node_modules/lodash/fp/omit.js create mode 100644 backend/node_modules/lodash/fp/omitAll.js create mode 100644 backend/node_modules/lodash/fp/omitBy.js create mode 100644 backend/node_modules/lodash/fp/once.js create mode 100644 backend/node_modules/lodash/fp/orderBy.js create mode 100644 backend/node_modules/lodash/fp/over.js create mode 100644 backend/node_modules/lodash/fp/overArgs.js create mode 100644 backend/node_modules/lodash/fp/overEvery.js create mode 100644 backend/node_modules/lodash/fp/overSome.js create mode 100644 backend/node_modules/lodash/fp/pad.js create mode 100644 backend/node_modules/lodash/fp/padChars.js create mode 100644 backend/node_modules/lodash/fp/padCharsEnd.js create mode 100644 backend/node_modules/lodash/fp/padCharsStart.js create mode 100644 backend/node_modules/lodash/fp/padEnd.js create mode 100644 backend/node_modules/lodash/fp/padStart.js create mode 100644 backend/node_modules/lodash/fp/parseInt.js create mode 100644 backend/node_modules/lodash/fp/partial.js create mode 100644 backend/node_modules/lodash/fp/partialRight.js create mode 100644 backend/node_modules/lodash/fp/partition.js create mode 100644 backend/node_modules/lodash/fp/path.js create mode 100644 backend/node_modules/lodash/fp/pathEq.js create mode 100644 backend/node_modules/lodash/fp/pathOr.js create mode 100644 backend/node_modules/lodash/fp/paths.js create mode 100644 backend/node_modules/lodash/fp/pick.js create mode 100644 backend/node_modules/lodash/fp/pickAll.js create mode 100644 backend/node_modules/lodash/fp/pickBy.js create mode 100644 backend/node_modules/lodash/fp/pipe.js create mode 100644 backend/node_modules/lodash/fp/placeholder.js create mode 100644 backend/node_modules/lodash/fp/plant.js create mode 100644 backend/node_modules/lodash/fp/pluck.js create mode 100644 backend/node_modules/lodash/fp/prop.js create mode 100644 backend/node_modules/lodash/fp/propEq.js create mode 100644 backend/node_modules/lodash/fp/propOr.js create mode 100644 backend/node_modules/lodash/fp/property.js create mode 100644 backend/node_modules/lodash/fp/propertyOf.js create mode 100644 backend/node_modules/lodash/fp/props.js create mode 100644 backend/node_modules/lodash/fp/pull.js create mode 100644 backend/node_modules/lodash/fp/pullAll.js create mode 100644 backend/node_modules/lodash/fp/pullAllBy.js create mode 100644 backend/node_modules/lodash/fp/pullAllWith.js create mode 100644 backend/node_modules/lodash/fp/pullAt.js create mode 100644 backend/node_modules/lodash/fp/random.js create mode 100644 backend/node_modules/lodash/fp/range.js create mode 100644 backend/node_modules/lodash/fp/rangeRight.js create mode 100644 backend/node_modules/lodash/fp/rangeStep.js create mode 100644 backend/node_modules/lodash/fp/rangeStepRight.js create mode 100644 backend/node_modules/lodash/fp/rearg.js create mode 100644 backend/node_modules/lodash/fp/reduce.js create mode 100644 backend/node_modules/lodash/fp/reduceRight.js create mode 100644 backend/node_modules/lodash/fp/reject.js create mode 100644 backend/node_modules/lodash/fp/remove.js create mode 100644 backend/node_modules/lodash/fp/repeat.js create mode 100644 backend/node_modules/lodash/fp/replace.js create mode 100644 backend/node_modules/lodash/fp/rest.js create mode 100644 backend/node_modules/lodash/fp/restFrom.js create mode 100644 backend/node_modules/lodash/fp/result.js create mode 100644 backend/node_modules/lodash/fp/reverse.js create mode 100644 backend/node_modules/lodash/fp/round.js create mode 100644 backend/node_modules/lodash/fp/sample.js create mode 100644 backend/node_modules/lodash/fp/sampleSize.js create mode 100644 backend/node_modules/lodash/fp/seq.js create mode 100644 backend/node_modules/lodash/fp/set.js create mode 100644 backend/node_modules/lodash/fp/setWith.js create mode 100644 backend/node_modules/lodash/fp/shuffle.js create mode 100644 backend/node_modules/lodash/fp/size.js create mode 100644 backend/node_modules/lodash/fp/slice.js create mode 100644 backend/node_modules/lodash/fp/snakeCase.js create mode 100644 backend/node_modules/lodash/fp/some.js create mode 100644 backend/node_modules/lodash/fp/sortBy.js create mode 100644 backend/node_modules/lodash/fp/sortedIndex.js create mode 100644 backend/node_modules/lodash/fp/sortedIndexBy.js create mode 100644 backend/node_modules/lodash/fp/sortedIndexOf.js create mode 100644 backend/node_modules/lodash/fp/sortedLastIndex.js create mode 100644 backend/node_modules/lodash/fp/sortedLastIndexBy.js create mode 100644 backend/node_modules/lodash/fp/sortedLastIndexOf.js create mode 100644 backend/node_modules/lodash/fp/sortedUniq.js create mode 100644 backend/node_modules/lodash/fp/sortedUniqBy.js create mode 100644 backend/node_modules/lodash/fp/split.js create mode 100644 backend/node_modules/lodash/fp/spread.js create mode 100644 backend/node_modules/lodash/fp/spreadFrom.js create mode 100644 backend/node_modules/lodash/fp/startCase.js create mode 100644 backend/node_modules/lodash/fp/startsWith.js create mode 100644 backend/node_modules/lodash/fp/string.js create mode 100644 backend/node_modules/lodash/fp/stubArray.js create mode 100644 backend/node_modules/lodash/fp/stubFalse.js create mode 100644 backend/node_modules/lodash/fp/stubObject.js create mode 100644 backend/node_modules/lodash/fp/stubString.js create mode 100644 backend/node_modules/lodash/fp/stubTrue.js create mode 100644 backend/node_modules/lodash/fp/subtract.js create mode 100644 backend/node_modules/lodash/fp/sum.js create mode 100644 backend/node_modules/lodash/fp/sumBy.js create mode 100644 backend/node_modules/lodash/fp/symmetricDifference.js create mode 100644 backend/node_modules/lodash/fp/symmetricDifferenceBy.js create mode 100644 backend/node_modules/lodash/fp/symmetricDifferenceWith.js create mode 100644 backend/node_modules/lodash/fp/tail.js create mode 100644 backend/node_modules/lodash/fp/take.js create mode 100644 backend/node_modules/lodash/fp/takeLast.js create mode 100644 backend/node_modules/lodash/fp/takeLastWhile.js create mode 100644 backend/node_modules/lodash/fp/takeRight.js create mode 100644 backend/node_modules/lodash/fp/takeRightWhile.js create mode 100644 backend/node_modules/lodash/fp/takeWhile.js create mode 100644 backend/node_modules/lodash/fp/tap.js create mode 100644 backend/node_modules/lodash/fp/template.js create mode 100644 backend/node_modules/lodash/fp/templateSettings.js create mode 100644 backend/node_modules/lodash/fp/throttle.js create mode 100644 backend/node_modules/lodash/fp/thru.js create mode 100644 backend/node_modules/lodash/fp/times.js create mode 100644 backend/node_modules/lodash/fp/toArray.js create mode 100644 backend/node_modules/lodash/fp/toFinite.js create mode 100644 backend/node_modules/lodash/fp/toInteger.js create mode 100644 backend/node_modules/lodash/fp/toIterator.js create mode 100644 backend/node_modules/lodash/fp/toJSON.js create mode 100644 backend/node_modules/lodash/fp/toLength.js create mode 100644 backend/node_modules/lodash/fp/toLower.js create mode 100644 backend/node_modules/lodash/fp/toNumber.js create mode 100644 backend/node_modules/lodash/fp/toPairs.js create mode 100644 backend/node_modules/lodash/fp/toPairsIn.js create mode 100644 backend/node_modules/lodash/fp/toPath.js create mode 100644 backend/node_modules/lodash/fp/toPlainObject.js create mode 100644 backend/node_modules/lodash/fp/toSafeInteger.js create mode 100644 backend/node_modules/lodash/fp/toString.js create mode 100644 backend/node_modules/lodash/fp/toUpper.js create mode 100644 backend/node_modules/lodash/fp/transform.js create mode 100644 backend/node_modules/lodash/fp/trim.js create mode 100644 backend/node_modules/lodash/fp/trimChars.js create mode 100644 backend/node_modules/lodash/fp/trimCharsEnd.js create mode 100644 backend/node_modules/lodash/fp/trimCharsStart.js create mode 100644 backend/node_modules/lodash/fp/trimEnd.js create mode 100644 backend/node_modules/lodash/fp/trimStart.js create mode 100644 backend/node_modules/lodash/fp/truncate.js create mode 100644 backend/node_modules/lodash/fp/unapply.js create mode 100644 backend/node_modules/lodash/fp/unary.js create mode 100644 backend/node_modules/lodash/fp/unescape.js create mode 100644 backend/node_modules/lodash/fp/union.js create mode 100644 backend/node_modules/lodash/fp/unionBy.js create mode 100644 backend/node_modules/lodash/fp/unionWith.js create mode 100644 backend/node_modules/lodash/fp/uniq.js create mode 100644 backend/node_modules/lodash/fp/uniqBy.js create mode 100644 backend/node_modules/lodash/fp/uniqWith.js create mode 100644 backend/node_modules/lodash/fp/uniqueId.js create mode 100644 backend/node_modules/lodash/fp/unnest.js create mode 100644 backend/node_modules/lodash/fp/unset.js create mode 100644 backend/node_modules/lodash/fp/unzip.js create mode 100644 backend/node_modules/lodash/fp/unzipWith.js create mode 100644 backend/node_modules/lodash/fp/update.js create mode 100644 backend/node_modules/lodash/fp/updateWith.js create mode 100644 backend/node_modules/lodash/fp/upperCase.js create mode 100644 backend/node_modules/lodash/fp/upperFirst.js create mode 100644 backend/node_modules/lodash/fp/useWith.js create mode 100644 backend/node_modules/lodash/fp/util.js create mode 100644 backend/node_modules/lodash/fp/value.js create mode 100644 backend/node_modules/lodash/fp/valueOf.js create mode 100644 backend/node_modules/lodash/fp/values.js create mode 100644 backend/node_modules/lodash/fp/valuesIn.js create mode 100644 backend/node_modules/lodash/fp/where.js create mode 100644 backend/node_modules/lodash/fp/whereEq.js create mode 100644 backend/node_modules/lodash/fp/without.js create mode 100644 backend/node_modules/lodash/fp/words.js create mode 100644 backend/node_modules/lodash/fp/wrap.js create mode 100644 backend/node_modules/lodash/fp/wrapperAt.js create mode 100644 backend/node_modules/lodash/fp/wrapperChain.js create mode 100644 backend/node_modules/lodash/fp/wrapperLodash.js create mode 100644 backend/node_modules/lodash/fp/wrapperReverse.js create mode 100644 backend/node_modules/lodash/fp/wrapperValue.js create mode 100644 backend/node_modules/lodash/fp/xor.js create mode 100644 backend/node_modules/lodash/fp/xorBy.js create mode 100644 backend/node_modules/lodash/fp/xorWith.js create mode 100644 backend/node_modules/lodash/fp/zip.js create mode 100644 backend/node_modules/lodash/fp/zipAll.js create mode 100644 backend/node_modules/lodash/fp/zipObj.js create mode 100644 backend/node_modules/lodash/fp/zipObject.js create mode 100644 backend/node_modules/lodash/fp/zipObjectDeep.js create mode 100644 backend/node_modules/lodash/fp/zipWith.js create mode 100644 backend/node_modules/lodash/fromPairs.js create mode 100644 backend/node_modules/lodash/function.js create mode 100644 backend/node_modules/lodash/functions.js create mode 100644 backend/node_modules/lodash/functionsIn.js create mode 100644 backend/node_modules/lodash/get.js create mode 100644 backend/node_modules/lodash/groupBy.js create mode 100644 backend/node_modules/lodash/gt.js create mode 100644 backend/node_modules/lodash/gte.js create mode 100644 backend/node_modules/lodash/has.js create mode 100644 backend/node_modules/lodash/hasIn.js create mode 100644 backend/node_modules/lodash/head.js create mode 100644 backend/node_modules/lodash/identity.js create mode 100644 backend/node_modules/lodash/inRange.js create mode 100644 backend/node_modules/lodash/includes.js create mode 100644 backend/node_modules/lodash/index.js create mode 100644 backend/node_modules/lodash/indexOf.js create mode 100644 backend/node_modules/lodash/initial.js create mode 100644 backend/node_modules/lodash/intersection.js create mode 100644 backend/node_modules/lodash/intersectionBy.js create mode 100644 backend/node_modules/lodash/intersectionWith.js create mode 100644 backend/node_modules/lodash/invert.js create mode 100644 backend/node_modules/lodash/invertBy.js create mode 100644 backend/node_modules/lodash/invoke.js create mode 100644 backend/node_modules/lodash/invokeMap.js create mode 100644 backend/node_modules/lodash/isArguments.js create mode 100644 backend/node_modules/lodash/isArray.js create mode 100644 backend/node_modules/lodash/isArrayBuffer.js create mode 100644 backend/node_modules/lodash/isArrayLike.js create mode 100644 backend/node_modules/lodash/isArrayLikeObject.js create mode 100644 backend/node_modules/lodash/isBoolean.js create mode 100644 backend/node_modules/lodash/isBuffer.js create mode 100644 backend/node_modules/lodash/isDate.js create mode 100644 backend/node_modules/lodash/isElement.js create mode 100644 backend/node_modules/lodash/isEmpty.js create mode 100644 backend/node_modules/lodash/isEqual.js create mode 100644 backend/node_modules/lodash/isEqualWith.js create mode 100644 backend/node_modules/lodash/isError.js create mode 100644 backend/node_modules/lodash/isFinite.js create mode 100644 backend/node_modules/lodash/isFunction.js create mode 100644 backend/node_modules/lodash/isInteger.js create mode 100644 backend/node_modules/lodash/isLength.js create mode 100644 backend/node_modules/lodash/isMap.js create mode 100644 backend/node_modules/lodash/isMatch.js create mode 100644 backend/node_modules/lodash/isMatchWith.js create mode 100644 backend/node_modules/lodash/isNaN.js create mode 100644 backend/node_modules/lodash/isNative.js create mode 100644 backend/node_modules/lodash/isNil.js create mode 100644 backend/node_modules/lodash/isNull.js create mode 100644 backend/node_modules/lodash/isNumber.js create mode 100644 backend/node_modules/lodash/isObject.js create mode 100644 backend/node_modules/lodash/isObjectLike.js create mode 100644 backend/node_modules/lodash/isPlainObject.js create mode 100644 backend/node_modules/lodash/isRegExp.js create mode 100644 backend/node_modules/lodash/isSafeInteger.js create mode 100644 backend/node_modules/lodash/isSet.js create mode 100644 backend/node_modules/lodash/isString.js create mode 100644 backend/node_modules/lodash/isSymbol.js create mode 100644 backend/node_modules/lodash/isTypedArray.js create mode 100644 backend/node_modules/lodash/isUndefined.js create mode 100644 backend/node_modules/lodash/isWeakMap.js create mode 100644 backend/node_modules/lodash/isWeakSet.js create mode 100644 backend/node_modules/lodash/iteratee.js create mode 100644 backend/node_modules/lodash/join.js create mode 100644 backend/node_modules/lodash/kebabCase.js create mode 100644 backend/node_modules/lodash/keyBy.js create mode 100644 backend/node_modules/lodash/keys.js create mode 100644 backend/node_modules/lodash/keysIn.js create mode 100644 backend/node_modules/lodash/lang.js create mode 100644 backend/node_modules/lodash/last.js create mode 100644 backend/node_modules/lodash/lastIndexOf.js create mode 100644 backend/node_modules/lodash/lodash.js create mode 100644 backend/node_modules/lodash/lodash.min.js create mode 100644 backend/node_modules/lodash/lowerCase.js create mode 100644 backend/node_modules/lodash/lowerFirst.js create mode 100644 backend/node_modules/lodash/lt.js create mode 100644 backend/node_modules/lodash/lte.js create mode 100644 backend/node_modules/lodash/map.js create mode 100644 backend/node_modules/lodash/mapKeys.js create mode 100644 backend/node_modules/lodash/mapValues.js create mode 100644 backend/node_modules/lodash/matches.js create mode 100644 backend/node_modules/lodash/matchesProperty.js create mode 100644 backend/node_modules/lodash/math.js create mode 100644 backend/node_modules/lodash/max.js create mode 100644 backend/node_modules/lodash/maxBy.js create mode 100644 backend/node_modules/lodash/mean.js create mode 100644 backend/node_modules/lodash/meanBy.js create mode 100644 backend/node_modules/lodash/memoize.js create mode 100644 backend/node_modules/lodash/merge.js create mode 100644 backend/node_modules/lodash/mergeWith.js create mode 100644 backend/node_modules/lodash/method.js create mode 100644 backend/node_modules/lodash/methodOf.js create mode 100644 backend/node_modules/lodash/min.js create mode 100644 backend/node_modules/lodash/minBy.js create mode 100644 backend/node_modules/lodash/mixin.js create mode 100644 backend/node_modules/lodash/multiply.js create mode 100644 backend/node_modules/lodash/negate.js create mode 100644 backend/node_modules/lodash/next.js create mode 100644 backend/node_modules/lodash/noop.js create mode 100644 backend/node_modules/lodash/now.js create mode 100644 backend/node_modules/lodash/nth.js create mode 100644 backend/node_modules/lodash/nthArg.js create mode 100644 backend/node_modules/lodash/number.js create mode 100644 backend/node_modules/lodash/object.js create mode 100644 backend/node_modules/lodash/omit.js create mode 100644 backend/node_modules/lodash/omitBy.js create mode 100644 backend/node_modules/lodash/once.js create mode 100644 backend/node_modules/lodash/orderBy.js create mode 100644 backend/node_modules/lodash/over.js create mode 100644 backend/node_modules/lodash/overArgs.js create mode 100644 backend/node_modules/lodash/overEvery.js create mode 100644 backend/node_modules/lodash/overSome.js create mode 100644 backend/node_modules/lodash/package.json create mode 100644 backend/node_modules/lodash/pad.js create mode 100644 backend/node_modules/lodash/padEnd.js create mode 100644 backend/node_modules/lodash/padStart.js create mode 100644 backend/node_modules/lodash/parseInt.js create mode 100644 backend/node_modules/lodash/partial.js create mode 100644 backend/node_modules/lodash/partialRight.js create mode 100644 backend/node_modules/lodash/partition.js create mode 100644 backend/node_modules/lodash/pick.js create mode 100644 backend/node_modules/lodash/pickBy.js create mode 100644 backend/node_modules/lodash/plant.js create mode 100644 backend/node_modules/lodash/property.js create mode 100644 backend/node_modules/lodash/propertyOf.js create mode 100644 backend/node_modules/lodash/pull.js create mode 100644 backend/node_modules/lodash/pullAll.js create mode 100644 backend/node_modules/lodash/pullAllBy.js create mode 100644 backend/node_modules/lodash/pullAllWith.js create mode 100644 backend/node_modules/lodash/pullAt.js create mode 100644 backend/node_modules/lodash/random.js create mode 100644 backend/node_modules/lodash/range.js create mode 100644 backend/node_modules/lodash/rangeRight.js create mode 100644 backend/node_modules/lodash/rearg.js create mode 100644 backend/node_modules/lodash/reduce.js create mode 100644 backend/node_modules/lodash/reduceRight.js create mode 100644 backend/node_modules/lodash/reject.js create mode 100644 backend/node_modules/lodash/remove.js create mode 100644 backend/node_modules/lodash/repeat.js create mode 100644 backend/node_modules/lodash/replace.js create mode 100644 backend/node_modules/lodash/rest.js create mode 100644 backend/node_modules/lodash/result.js create mode 100644 backend/node_modules/lodash/reverse.js create mode 100644 backend/node_modules/lodash/round.js create mode 100644 backend/node_modules/lodash/sample.js create mode 100644 backend/node_modules/lodash/sampleSize.js create mode 100644 backend/node_modules/lodash/seq.js create mode 100644 backend/node_modules/lodash/set.js create mode 100644 backend/node_modules/lodash/setWith.js create mode 100644 backend/node_modules/lodash/shuffle.js create mode 100644 backend/node_modules/lodash/size.js create mode 100644 backend/node_modules/lodash/slice.js create mode 100644 backend/node_modules/lodash/snakeCase.js create mode 100644 backend/node_modules/lodash/some.js create mode 100644 backend/node_modules/lodash/sortBy.js create mode 100644 backend/node_modules/lodash/sortedIndex.js create mode 100644 backend/node_modules/lodash/sortedIndexBy.js create mode 100644 backend/node_modules/lodash/sortedIndexOf.js create mode 100644 backend/node_modules/lodash/sortedLastIndex.js create mode 100644 backend/node_modules/lodash/sortedLastIndexBy.js create mode 100644 backend/node_modules/lodash/sortedLastIndexOf.js create mode 100644 backend/node_modules/lodash/sortedUniq.js create mode 100644 backend/node_modules/lodash/sortedUniqBy.js create mode 100644 backend/node_modules/lodash/split.js create mode 100644 backend/node_modules/lodash/spread.js create mode 100644 backend/node_modules/lodash/startCase.js create mode 100644 backend/node_modules/lodash/startsWith.js create mode 100644 backend/node_modules/lodash/string.js create mode 100644 backend/node_modules/lodash/stubArray.js create mode 100644 backend/node_modules/lodash/stubFalse.js create mode 100644 backend/node_modules/lodash/stubObject.js create mode 100644 backend/node_modules/lodash/stubString.js create mode 100644 backend/node_modules/lodash/stubTrue.js create mode 100644 backend/node_modules/lodash/subtract.js create mode 100644 backend/node_modules/lodash/sum.js create mode 100644 backend/node_modules/lodash/sumBy.js create mode 100644 backend/node_modules/lodash/tail.js create mode 100644 backend/node_modules/lodash/take.js create mode 100644 backend/node_modules/lodash/takeRight.js create mode 100644 backend/node_modules/lodash/takeRightWhile.js create mode 100644 backend/node_modules/lodash/takeWhile.js create mode 100644 backend/node_modules/lodash/tap.js create mode 100644 backend/node_modules/lodash/template.js create mode 100644 backend/node_modules/lodash/templateSettings.js create mode 100644 backend/node_modules/lodash/throttle.js create mode 100644 backend/node_modules/lodash/thru.js create mode 100644 backend/node_modules/lodash/times.js create mode 100644 backend/node_modules/lodash/toArray.js create mode 100644 backend/node_modules/lodash/toFinite.js create mode 100644 backend/node_modules/lodash/toInteger.js create mode 100644 backend/node_modules/lodash/toIterator.js create mode 100644 backend/node_modules/lodash/toJSON.js create mode 100644 backend/node_modules/lodash/toLength.js create mode 100644 backend/node_modules/lodash/toLower.js create mode 100644 backend/node_modules/lodash/toNumber.js create mode 100644 backend/node_modules/lodash/toPairs.js create mode 100644 backend/node_modules/lodash/toPairsIn.js create mode 100644 backend/node_modules/lodash/toPath.js create mode 100644 backend/node_modules/lodash/toPlainObject.js create mode 100644 backend/node_modules/lodash/toSafeInteger.js create mode 100644 backend/node_modules/lodash/toString.js create mode 100644 backend/node_modules/lodash/toUpper.js create mode 100644 backend/node_modules/lodash/transform.js create mode 100644 backend/node_modules/lodash/trim.js create mode 100644 backend/node_modules/lodash/trimEnd.js create mode 100644 backend/node_modules/lodash/trimStart.js create mode 100644 backend/node_modules/lodash/truncate.js create mode 100644 backend/node_modules/lodash/unary.js create mode 100644 backend/node_modules/lodash/unescape.js create mode 100644 backend/node_modules/lodash/union.js create mode 100644 backend/node_modules/lodash/unionBy.js create mode 100644 backend/node_modules/lodash/unionWith.js create mode 100644 backend/node_modules/lodash/uniq.js create mode 100644 backend/node_modules/lodash/uniqBy.js create mode 100644 backend/node_modules/lodash/uniqWith.js create mode 100644 backend/node_modules/lodash/uniqueId.js create mode 100644 backend/node_modules/lodash/unset.js create mode 100644 backend/node_modules/lodash/unzip.js create mode 100644 backend/node_modules/lodash/unzipWith.js create mode 100644 backend/node_modules/lodash/update.js create mode 100644 backend/node_modules/lodash/updateWith.js create mode 100644 backend/node_modules/lodash/upperCase.js create mode 100644 backend/node_modules/lodash/upperFirst.js create mode 100644 backend/node_modules/lodash/util.js create mode 100644 backend/node_modules/lodash/value.js create mode 100644 backend/node_modules/lodash/valueOf.js create mode 100644 backend/node_modules/lodash/values.js create mode 100644 backend/node_modules/lodash/valuesIn.js create mode 100644 backend/node_modules/lodash/without.js create mode 100644 backend/node_modules/lodash/words.js create mode 100644 backend/node_modules/lodash/wrap.js create mode 100644 backend/node_modules/lodash/wrapperAt.js create mode 100644 backend/node_modules/lodash/wrapperChain.js create mode 100644 backend/node_modules/lodash/wrapperLodash.js create mode 100644 backend/node_modules/lodash/wrapperReverse.js create mode 100644 backend/node_modules/lodash/wrapperValue.js create mode 100644 backend/node_modules/lodash/xor.js create mode 100644 backend/node_modules/lodash/xorBy.js create mode 100644 backend/node_modules/lodash/xorWith.js create mode 100644 backend/node_modules/lodash/zip.js create mode 100644 backend/node_modules/lodash/zipObject.js create mode 100644 backend/node_modules/lodash/zipObjectDeep.js create mode 100644 backend/node_modules/lodash/zipWith.js create mode 100644 backend/node_modules/magic-string/LICENSE create mode 100644 backend/node_modules/magic-string/README.md create mode 100644 backend/node_modules/magic-string/package.json create mode 100644 backend/node_modules/nanoid/LICENSE create mode 100644 backend/node_modules/nanoid/README.md create mode 100644 backend/node_modules/nanoid/async/index.browser.cjs create mode 100644 backend/node_modules/nanoid/async/index.browser.js create mode 100644 backend/node_modules/nanoid/async/index.cjs create mode 100644 backend/node_modules/nanoid/async/index.d.ts create mode 100644 backend/node_modules/nanoid/async/index.js create mode 100644 backend/node_modules/nanoid/async/index.native.js create mode 100644 backend/node_modules/nanoid/async/package.json create mode 100755 backend/node_modules/nanoid/bin/nanoid.cjs create mode 100644 backend/node_modules/nanoid/index.browser.cjs create mode 100644 backend/node_modules/nanoid/index.browser.js create mode 100644 backend/node_modules/nanoid/index.cjs create mode 100644 backend/node_modules/nanoid/index.d.cts create mode 100644 backend/node_modules/nanoid/index.d.ts create mode 100644 backend/node_modules/nanoid/index.js create mode 100644 backend/node_modules/nanoid/nanoid.js create mode 100644 backend/node_modules/nanoid/non-secure/index.cjs create mode 100644 backend/node_modules/nanoid/non-secure/index.d.ts create mode 100644 backend/node_modules/nanoid/non-secure/index.js create mode 100644 backend/node_modules/nanoid/non-secure/package.json create mode 100644 backend/node_modules/nanoid/package.json create mode 100644 backend/node_modules/nanoid/url-alphabet/index.cjs create mode 100644 backend/node_modules/nanoid/url-alphabet/index.js create mode 100644 backend/node_modules/nanoid/url-alphabet/package.json create mode 100644 backend/node_modules/oauth4webapi/LICENSE.md create mode 100644 backend/node_modules/oauth4webapi/README.md create mode 100644 backend/node_modules/oauth4webapi/package.json create mode 100644 backend/node_modules/openid-client/LICENSE.md create mode 100644 backend/node_modules/openid-client/README.md create mode 100644 backend/node_modules/openid-client/package.json create mode 100644 backend/node_modules/picocolors/LICENSE create mode 100644 backend/node_modules/picocolors/README.md create mode 100644 backend/node_modules/picocolors/package.json create mode 100644 backend/node_modules/picocolors/picocolors.browser.js create mode 100644 backend/node_modules/picocolors/picocolors.d.ts create mode 100644 backend/node_modules/picocolors/picocolors.js create mode 100644 backend/node_modules/picocolors/types.d.ts create mode 100644 backend/node_modules/postcss/LICENSE create mode 100644 backend/node_modules/postcss/README.md create mode 100644 backend/node_modules/postcss/package.json create mode 100644 backend/node_modules/source-map-js/LICENSE create mode 100644 backend/node_modules/source-map-js/README.md create mode 100644 backend/node_modules/source-map-js/package.json create mode 100644 backend/node_modules/source-map-js/source-map.d.ts create mode 100644 backend/node_modules/source-map-js/source-map.js create mode 100644 backend/node_modules/vue/LICENSE create mode 100644 backend/node_modules/vue/README.md create mode 100644 backend/node_modules/vue/compiler-sfc/index.browser.js create mode 100644 backend/node_modules/vue/compiler-sfc/index.browser.mjs create mode 100644 backend/node_modules/vue/compiler-sfc/index.d.mts create mode 100644 backend/node_modules/vue/compiler-sfc/index.d.ts create mode 100644 backend/node_modules/vue/compiler-sfc/index.js create mode 100644 backend/node_modules/vue/compiler-sfc/index.mjs create mode 100644 backend/node_modules/vue/compiler-sfc/package.json create mode 100644 backend/node_modules/vue/compiler-sfc/register-ts.js create mode 100644 backend/node_modules/vue/index.js create mode 100644 backend/node_modules/vue/index.mjs create mode 100644 backend/node_modules/vue/jsx-runtime/index.d.ts create mode 100644 backend/node_modules/vue/jsx-runtime/index.js create mode 100644 backend/node_modules/vue/jsx-runtime/index.mjs create mode 100644 backend/node_modules/vue/jsx-runtime/package.json create mode 100644 backend/node_modules/vue/jsx.d.ts create mode 100644 backend/node_modules/vue/package.json create mode 100644 backend/node_modules/vue/server-renderer/index.d.mts create mode 100644 backend/node_modules/vue/server-renderer/index.d.ts create mode 100644 backend/node_modules/vue/server-renderer/index.js create mode 100644 backend/node_modules/vue/server-renderer/index.mjs create mode 100644 backend/node_modules/vue/server-renderer/package.json create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/poetry.lock create mode 100644 frontend/app/components/AuthMenu.client.vue create mode 100644 frontend/app/middleware/auth.ts create mode 100644 frontend/app/pages/login.vue create mode 100644 frontend/app/pages/my-runs.vue create mode 100644 frontend/app/plugins/auth0.client.ts create mode 100644 package-lock.json diff --git a/backend/.env.example b/backend/.env.example index 9a498a1d..06d73c4e 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -37,3 +37,7 @@ MONGODB_URI=mongodb://localhost:27017 # this — the built-in list already covers localhost ports. Deployments # (kustomize overlays) set this to the deployed frontend host(s). # CORS_EXTRA_ORIGINS=https://example.org,https://example-dev.org + +AUTH0_DOMAIN=dev-bu7yo7484tyxu6a1.us.auth0.com +AUTH0_AUDIENCE=https://api.biosimulations.org +AUTH0_ISSUER=https://dev-bu7yo7484tyxu6a1.us.auth0.com/ diff --git a/backend/biosim_server/common/auth/auth0.py b/backend/biosim_server/common/auth/auth0.py new file mode 100644 index 00000000..cba644d8 --- /dev/null +++ b/backend/biosim_server/common/auth/auth0.py @@ -0,0 +1,75 @@ +import time +from functools import lru_cache + +import httpx +from fastapi import Depends, HTTPException, status +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer +from jose import jwt + +from biosim_server.config import get_settings + +bearer_scheme = HTTPBearer(auto_error=False) + +_jwks_cache: dict = {"keys": None, "fetched_at": 0.0} +_JWKS_TTL_SECONDS = 3600 + + +def _get_jwks() -> dict: + settings = get_settings().auth0 + now = time.time() + if _jwks_cache["keys"] is None or now - _jwks_cache["fetched_at"] > _JWKS_TTL_SECONDS: + resp = httpx.get(f"https://{settings.domain}/.well-known/jwks.json", timeout=5.0) + resp.raise_for_status() + _jwks_cache["keys"] = resp.json() + _jwks_cache["fetched_at"] = now + return _jwks_cache["keys"] + + +class AuthenticatedUser: + def __init__(self, sub: str, email: str | None): + self.sub = sub # stable Auth0 user id, e.g. "auth0|abc123" or "google-oauth2|..." + self.email = email + + +def get_current_user( + credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme), +) -> AuthenticatedUser: + if credentials is None: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Missing bearer token") + + settings = get_settings().auth0 + token = credentials.credentials + + try: + unverified_header = jwt.get_unverified_header(token) + except Exception: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Malformed token") + + jwks = _get_jwks() + rsa_key = next( + ( + {"kty": k["kty"], "kid": k["kid"], "use": k["use"], "n": k["n"], "e": k["e"]} + for k in jwks["keys"] + if k["kid"] == unverified_header.get("kid") + ), + None, + ) + if rsa_key is None: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Unknown signing key") + + try: + payload = jwt.decode( + token, + rsa_key, + algorithms=settings.algorithms, + audience=settings.audience, + issuer=f"https://{settings.domain}/", + ) + except jwt.ExpiredSignatureError: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Token expired") + except jwt.JWTClaimsError: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid claims") + except Exception: + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid token") + + return AuthenticatedUser(sub=payload["sub"], email=payload.get("email")) diff --git a/backend/biosim_server/config.py b/backend/biosim_server/config.py index d3f0081e..bec90944 100644 --- a/backend/biosim_server/config.py +++ b/backend/biosim_server/config.py @@ -21,6 +21,13 @@ if os.getenv(ENV_SECRET_ENV_FILE) is not None and os.path.exists(str(os.getenv(ENV_SECRET_ENV_FILE))): load_dotenv(os.getenv(ENV_SECRET_ENV_FILE)) +class Auth0Settings(BaseSettings): + domain: str = Field(..., alias="AUTH0_DOMAIN"") + audience: str = Field(..., alias="AUTH0_AUDIENCE") + algorithms = list[str] = ["RS256"] + + model_config = SettingsConfigDict(env_prefix"", extra="ignore") + class Settings(BaseSettings): storage_backend: STORAGE_BACKEND = "gcs" @@ -88,6 +95,9 @@ class Settings(BaseSettings): simdata_api_base_url: str = "https://simdata.api.biosimulations.org" biosimulators_api_base_url: str = "https://api.biosimulators.org" biosimulations_api_base_url: str = "https://api.biosimulations.org" + auth0_domain: str = "" + auth0_audience: str = "" + auth0_issuer: str = "" slurm_submit_host: str = "" # "hamantis.cam.uchc.edu" slurm_submit_user: str = "" # "crbmapi" @@ -108,4 +118,3 @@ def get_local_cache_dir() -> Path: local_cache_dir = Path(settings.storage_local_cache_dir) local_cache_dir.mkdir(parents=True, exist_ok=True) return local_cache_dir - diff --git a/backend/biosim_server/dependencies.py b/backend/biosim_server/dependencies.py index e2108ba7..dee14267 100644 --- a/backend/biosim_server/dependencies.py +++ b/backend/biosim_server/dependencies.py @@ -8,6 +8,7 @@ from biosim_server.biosim_runs.database import DatabaseService, DatabaseServiceMongo from biosim_server.common.storage import FileService, FileServiceGCS, FileServiceLocal, FileServiceMinio from biosim_server.config import get_local_cache_dir, get_settings +from biosim_server.common.auth import get_current_user if TYPE_CHECKING: # Imported lazily at runtime (see init_standalone) to avoid an import cycle: @@ -163,4 +164,4 @@ async def shutdown_standalone() -> None: set_database_service(None) # Shares the motor client closed via db_service above; just clear the handles. set_simulation_run_database_service(None) - set_project_database_service(None) \ No newline at end of file + set_project_database_service(None) diff --git a/backend/biosim_server/simulations/router.py b/backend/biosim_server/simulations/router.py index f2bc4cdc..935dc902 100644 --- a/backend/biosim_server/simulations/router.py +++ b/backend/biosim_server/simulations/router.py @@ -23,6 +23,8 @@ ) from biosim_server.simulations.workflow import SimulationRunWorkflow, SimulationRunWorkflowInput +from biosim_server.common.auth.auth0 import AuthenticatedUser, get_current_user + logger = logging.getLogger(__name__) router = APIRouter(prefix="/simulations", tags=["Simulations"]) @@ -140,7 +142,7 @@ async def run_simulations(request: RunSimulationRequest) -> ConglomerateStatus: ] return ConglomerateStatus(processing_id=workflow_id, jobs=jobs) - +''' @router.post( "/runs", response_model=ListSimulationRunsResponse, @@ -160,7 +162,16 @@ async def list_simulation_runs(request: ListSimulationRunsRequest) -> ListSimula runs=[SimulationRun.from_record(record) for record in records], pagination=request.pagination.model_copy(update={"total": total}), ) - +''' + +@router.post("/simulations/runs") +async def get_runs( + query: RunsQuery, + user: AuthenticatedUser = Depends(get_current_user), +): + if query.type == "user": + query.user = user.email +) @router.get( "/{processing_id}", diff --git a/backend/biosim_server/worker/worker_main.py b/backend/biosim_server/worker/worker_main.py index fcdd4cac..f36ddcba 100644 --- a/backend/biosim_server/worker/worker_main.py +++ b/backend/biosim_server/worker/worker_main.py @@ -43,10 +43,8 @@ async def main() -> None: if __name__ == "__main__": - loop = asyncio.get_event_loop() try: - loop.run_until_complete(main()) + asyncio.run(main()) except KeyboardInterrupt: interrupt_event.set() - loop.run_until_complete(loop.shutdown_asyncgens()) print("\nShutting down workers") diff --git a/backend/node_modules/.bin/nanoid b/backend/node_modules/.bin/nanoid new file mode 120000 index 00000000..e2be547b --- /dev/null +++ b/backend/node_modules/.bin/nanoid @@ -0,0 +1 @@ +../nanoid/bin/nanoid.cjs \ No newline at end of file diff --git a/backend/node_modules/.bin/parser b/backend/node_modules/.bin/parser new file mode 120000 index 00000000..ce7bf97e --- /dev/null +++ b/backend/node_modules/.bin/parser @@ -0,0 +1 @@ +../@babel/parser/bin/babel-parser.js \ No newline at end of file diff --git a/backend/node_modules/.package-lock.json b/backend/node_modules/.package-lock.json new file mode 100644 index 00000000..4016440a --- /dev/null +++ b/backend/node_modules/.package-lock.json @@ -0,0 +1,374 @@ +{ + "name": "backend", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@auth0/auth0-auth-js": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-auth-js/-/auth0-auth-js-1.12.0.tgz", + "integrity": "sha512-94r29UxcFEdroO74TUqu5KYy5hSKa6mCXY8lu4hADiyES8qthlSCwaCPEW1adpM5hdheltQ7s1o9TsK8Btyjvg==", + "license": "MIT", + "dependencies": { + "jose": "^6.0.8", + "openid-client": "^6.8.0" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "2.24.1", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.24.1.tgz", + "integrity": "sha512-iS18fBlWWxQ/FPtWgWzYYEImsNRumJG2cZ08xV0HsEmWtTBv+54A+fnyFx9eiNBP7cLp0emwDDjgeG+hQEbVBA==", + "license": "MIT", + "dependencies": { + "@auth0/auth0-auth-js": "^1.10.0", + "browser-tabs-lock": "^1.3.0", + "dpop": "^2.1.1", + "es-cookie": "~1.3.2" + } + }, + "node_modules/@auth0/auth0-vue": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-vue/-/auth0-vue-2.9.0.tgz", + "integrity": "sha512-1q1OexmB1UR+Xxo1HNQZi4ZF88sbvDCdBhI4cdPQxmL8gF7bInV8CfxsYX3eDHD6Vs74wCNVRSFy4wnuh3Pm6g==", + "license": "MIT", + "dependencies": { + "@auth0/auth0-spa-js": "^2.23.0", + "vue": "^3.5.21" + }, + "peerDependencies": { + "vue-router": "^4.0.12 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vue-router": { + "optional": true + } + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz", + "integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.40", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz", + "integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz", + "integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.40", + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-ssr": "3.5.40", + "@vue/shared": "3.5.40", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz", + "integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz", + "integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz", + "integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz", + "integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/runtime-core": "3.5.40", + "@vue/shared": "3.5.40", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz", + "integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz", + "integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==", + "license": "MIT" + }, + "node_modules/browser-tabs-lock": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.3.0.tgz", + "integrity": "sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "lodash": ">=4.17.21" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dpop": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dpop/-/dpop-2.1.1.tgz", + "integrity": "sha512-J0Of2JTiM4h5si0tlbPQ/lkqfZ5wAEVkKYBhkwyyANnPJfWH4VsR5uIkZ+T+OSPIwDYUg1fbd5Mmodd25HjY1w==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-cookie": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz", + "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==", + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/jose": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.8.tgz", + "integrity": "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oauth4webapi": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.6.tgz", + "integrity": "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz", + "integrity": "sha512-QSw0BA08piujetEwfZsHoTrDpMEha7GDZDicQqVwX4u0ChCjefvjDB++TZ8BTg76UpwhzIQgdvvfgfl3HpCSAw==", + "license": "MIT", + "dependencies": { + "jose": "^6.2.2", + "oauth4webapi": "^3.8.5" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz", + "integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-sfc": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/server-renderer": "3.5.40", + "@vue/shared": "3.5.40" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + } + } +} diff --git a/backend/node_modules/@auth0/auth0-auth-js/LICENSE b/backend/node_modules/@auth0/auth0-auth-js/LICENSE new file mode 100644 index 00000000..e79ec265 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-auth-js/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2025 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-auth-js/README.md b/backend/node_modules/@auth0/auth0-auth-js/README.md new file mode 100644 index 00000000..f6a148a7 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-auth-js/README.md @@ -0,0 +1,364 @@ +The `@auth0/auth0-auth-js` library provides API's to interact with Auth0's Authentication Api's from withing JavaScript applications. + +It contains methods to build Authorization URLs and Logout URLs, implement Backchannel Logout, verifying a logout token, to request Tokens using the Authorization Code Flow and Refresh Tokens, as well as retrieving a Token for a Connection, and managing Multi-Factor Authentication (MFA). + + +![Release](https://img.shields.io/npm/v/@auth0/auth0-auth-js) +![Downloads](https://img.shields.io/npm/dw/@auth0/auth0-auth-js) +[![License](https://img.shields.io/:license-mit-blue.svg?style=flat)](https://opensource.org/licenses/MIT) + +📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started) - 💬 [Feedback](#feedback) + +## Documentation + +- [Examples](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-auth-js/EXAMPLES.md) - examples for your different use cases. +- [Docs Site](https://auth0.com/docs) - explore our docs site and learn more about Auth0. + +## Getting Started + +### 1. Install the SDK + +```shell +npm i @auth0/auth0-auth-js +``` + +This library requires Node.js 20 LTS and newer LTS versions. + +### 2. Create the Auth0 SDK client + +Create an instance of the `AuthClient`. This instance will be imported and used anywhere we need access to the authentication methods. + + +```ts +import { AuthClient } from '@auth0/auth0-auth-js'; + +const authClient = new AuthClient({ + domain: '', + clientId: '', + clientSecret: '', +}); +``` + +The `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET` can be obtained from the [Auth0 Dashboard](https://manage.auth0.com) once you've created an application. + +### 3. Build the Authorization URL + +Build the URL to redirect the user-agent to to request authorization at Auth0. + +```ts +const authClient = new AuthClient({ + // ... + authorizationParams: { + redirect_uri: '', + }, + // ... +}); + +The `AUTH0_REDIRECT_URI` is needed to tell Auth0 what URL to redirect back to after successfull authentication, e.g. `http://localhost:3000/auth/callback`. +``` + +> [!IMPORTANT] +> You will need to register the `AUTH0_REDIRECT_URI` in your Auth0 Application as an **Allowed Callback URL** via the [Auth0 Dashboard](https://manage.auth0.com). + +In order to build the authorization URL, call `buildAuthorizationUrl()`, and redirect the user to the returned URL. + +```ts +const { authorizationUrl, codeVerifier } = await authClient.buildAuthorizationUrl(); +``` + +- `authorizationUrl`: The URL to redirect the user to. +- `codeVerifier`: The code verifier that should be stored and used when exchanging the code for tokens. + +### 4. Build the Logout URL + +Build the URL to redirect the user-agent to to request logout at Auth0. + +```ts +const logoutUrl = authClient.buildLogoutUrl({ + returnTo: '', +}); +``` + +> [!IMPORTANT] +> You will need to register the `AUTH0_LOGOUT_RETURN_URL` in your Auth0 Application as an **Allowed Logout URL** via the [Auth0 Dashboard](https://manage.auth0.com). + +The `AUTH0_LOGOUT_RETURN_URL` is needed to tell Auth0 what URL to redirect back to after successfully logging out, e.g. `http://localhost:3000`. + +### 5. Token Exchange + +The SDK supports RFC 8693 OAuth 2.0 Token Exchange for first-party on-behalf-of flows, enabling secure token exchanges while preserving user identity. + +#### When to Use Which Flow + +- **Custom Token Exchange**: Use when you control the subject token format. Common scenarios: + - Exchanging MCP server tokens for Auth0 tokens + - Migrating from legacy authentication systems + - Federating with partner systems using custom token formats + - Exchanging tokens issued by your own services + +- **Access Token Exchange with Token Vault** (via `exchangeToken`): Use when exchanging for external provider's access tokens: + - Accessing Google APIs with a user's Google token + - Calling Facebook Graph API with a user's Facebook token + - Any scenario where Auth0 manages the external provider's refresh tokens in the Token Vault + +> **Deprecated:** `getTokenForConnection()` is deprecated. Use `exchangeToken({ connection, subjectToken, subjectTokenType, ... })` instead. + +#### Custom Token Exchange Example + +> **Note**: In this SDK, Custom Token Exchange currently requires a confidential client. Supported client authentication methods: `client_secret_post`, `private_key_jwt`, and `mTLS` (via `customFetch`). Public clients are not yet supported by this method. + +```ts +import { AuthClient } from '@auth0/auth0-auth-js'; + +const authClient = new AuthClient({ + domain: '', + clientId: '', + clientSecret: '', +}); + +// Exchange a custom token (e.g., from an MCP server or legacy system) +// The subjectTokenType identifies your token format (configured in your Token Exchange Profile) +const response = await authClient.exchangeToken({ + subjectTokenType: 'urn:example:custom-token', // Your custom token type URN + subjectToken: userAccessToken, // The token to exchange + audience: 'https://api.backend.com', +}); + +// Handle token expiry - check expiresAt and re-exchange when needed +// Note: expiresAt is in seconds, Date.now() is in milliseconds +const tokenIsValid = Math.floor(Date.now() / 1000) < response.expiresAt; +if (!tokenIsValid) { + // Re-exchange the token or use a refresh token if available + const refreshed = await authClient.exchangeToken({ + subjectTokenType: 'urn:example:custom-token', + subjectToken: newSubjectToken, + audience: 'https://api.backend.com', + }); +} +``` + +> **Security Note**: Never include PII, secrets, or sensitive data in the `extra` parameter. +> These values may be logged by Auth0 or intermediary systems. Use `extra` only for +> non-sensitive metadata like device IDs, session identifiers, or request context. + +#### Token Vault Example + +```ts +import { AuthClient } from '@auth0/auth0-auth-js'; + +const authClient = new AuthClient({ + domain: '', + clientId: '', + clientSecret: '', +}); + +// Exchange an Auth0 access token for an external provider's access token (e.g., Google) +const response = await authClient.exchangeToken({ + connection: 'google-oauth2', + subjectToken: auth0AccessToken, + subjectTokenType: 'urn:ietf:params:oauth:token-type:access_token', + loginHint: 'user@example.com', // Optional: specify which account when user has multiple + scope: 'https://www.googleapis.com/auth/calendar.readonly', // Optional: specific scopes +}); + +// Or exchange an Auth0 refresh token instead +const responseFromRefresh = await authClient.exchangeToken({ + connection: 'google-oauth2', + subjectToken: auth0RefreshToken, + subjectTokenType: 'urn:ietf:params:oauth:token-type:refresh_token', +}); + +// Use the external provider's access token +console.log('External access token:', response.accessToken); +``` + +
+Migration from deprecated getTokenForConnection() + +```ts +// ❌ Deprecated (still works, but will be removed in v2.0) +const response = await authClient.getTokenForConnection({ + connection: 'google-oauth2', + accessToken: auth0AccessToken, + loginHint: 'user@example.com', +}); + +// ✅ New unified API +const response = await authClient.exchangeToken({ + connection: 'google-oauth2', + subjectToken: auth0AccessToken, + subjectTokenType: 'urn:ietf:params:oauth:token-type:access_token', + loginHint: 'user@example.com', +}); +``` + +
+ +Learn more: [Custom Token Exchange](https://auth0.com/docs/authenticate/custom-token-exchange) | [Token Vault](https://auth0.com/docs/secure/tokens/token-vault/access-token-exchange-with-token-vault) + +### 6. Resource Owner Password Grant + +> [!WARNING] +> This flow should only be used from highly-trusted applications that cannot do redirects. If you can use redirect-based flows from your app, we recommend using the Authorization Code Flow instead. + +The SDK supports Resource Owner Password Grant (ROPG) for scenarios where users authenticate by providing their username and password directly: + +```ts +const tokenResponse = await authClient.getTokenByPassword({ + username: 'user@example.com', + password: 'password123', + realm: 'Username-Password-Authentication', // Optional: database connection name + audience: 'https://api.example.com', // Optional: API identifier + scope: 'openid profile email', // Optional: requested scopes +}); + +console.log('Access token:', tokenResponse.accessToken); +``` + +For server-side applications, you can pass the end-user's IP address for brute-force protection: + +```ts +const tokenResponse = await authClient.getTokenByPassword({ + username: 'user@example.com', + password: 'password123', + auth0ForwardedFor: req.ip, // End-user's IP address +}); +``` + +Learn more: [Resource Owner Password Flow](https://auth0.com/docs/api/authentication/resource-owner-password-flow/get-token) + +### 7. Multi-Factor Authentication (MFA) + +The SDK provides built-in support for managing Multi-Factor Authentication. You can enroll authenticators (OTP, SMS, email), list enrolled authenticators, challenge them for verification, and delete them. + +```ts +// Access the MFA client via the authClient.mfa property +const mfaToken = ''; + +// Enroll an OTP authenticator (Google Authenticator, Auth0, etc.) +const enrollment = await authClient.mfa.enrollAuthenticator({ + authenticatorTypes: ['otp'], + mfaToken +}); + +// List all enrolled authenticators +const authenticators = await authClient.mfa.listAuthenticators({ mfaToken }); + +// Challenge an authenticator +const challenge = await authClient.mfa.challengeAuthenticator({ + challengeType: 'otp', + mfaToken +}); + +// Delete an authenticator +await authClient.mfa.deleteAuthenticator({ + authenticatorId: 'totp|dev_abc123', + mfaToken +}); +``` + +For detailed MFA examples including SMS enrollment, OOB challenges, and more, see the [MFA section in EXAMPLES.md](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-auth-js/EXAMPLES.md#using-multi-factor-authentication-mfa). + +### 8. Passkeys + +The SDK provides native support for passkey-based authentication using the WebAuthn protocol. You can register new passkeys, authenticate with existing passkeys, and exchange passkey credentials for tokens — all without redirect-based flows. + +```ts +import { AuthClient, PasskeyGetTokenError } from '@auth0/auth0-auth-js'; + +const authClient = new AuthClient({ + domain: '', + clientId: '', + // Required for getTokenByPasskey() (step 3) — it needs a confidential client. + // register() and challenge() also work with public clients (no secret). + clientSecret: '', +}); + +// 1. Register a new passkey (signup) — works with public or confidential clients +const signupChallenge = await authClient.passkey.register({ + email: 'user@example.com', + name: 'Jane Doe', +}); +// Pass signupChallenge.authnParamsPublicKey to navigator.credentials.create() + +// 2. Authenticate with an existing passkey (login) — works with public or confidential clients +const loginChallenge = await authClient.passkey.challenge(); +// Pass loginChallenge.authnParamsPublicKey to navigator.credentials.get() + +// 3. Exchange the serialized credential response for tokens — confidential client only +const tokens = await authClient.passkey.getTokenByPasskey({ + authSession: signupChallenge.authSession, + credential: serializedCredential, + audience: 'https://api.example.com', + scope: 'openid profile email', +}); +``` + +> [!IMPORTANT] +> `getTokenByPasskey()` requires a **confidential client** (`clientSecret` or `clientAssertionSigningKey`); only `register()` and `challenge()` work with public clients. Passkeys also require the following prerequisites: +> - A [custom domain](https://auth0.com/docs/customize/custom-domains) configured on your Auth0 tenant (e.g., `auth.example.com`, not `example.auth0.com`). The custom domain serves as the WebAuthn Relying Party (RP) ID, which must match or be a registrable domain suffix of your application's origin. +> - A database connection with the `passkey` authentication method enabled. +> - Your application must be served over HTTPS on a domain that aligns with the configured RP ID. + +For detailed passkey examples including credential serialization, all parameter options, and error handling, see the [Passkeys section in EXAMPLES.md](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-auth-js/EXAMPLES.md#using-passkeys). + +Learn more: [Passkeys](https://auth0.com/docs/authenticate/database-connections/passkeys) | [Native Passkeys API](https://auth0.com/docs/authenticate/database-connections/passkeys/native-passkeys-api) + +### 9. Passwordless OTP on Database Connections + +For applications using database connections (rather than dedicated passwordless connections), the SDK supports an embedded OTP challenge flow: + +```ts +// Step 1: Request an OTP challenge +const challenge = await authClient.passwordless.challengeWithEmail({ + email: 'user@example.com', + connection: 'my-db-connection', +}); + +// Step 2: Exchange the OTP for tokens +const tokens = await authClient.passwordless.getTokenByPasswordlessDbConnection({ + authSession: challenge.authSession, + otp: userEnteredCode, + scope: 'openid profile', +}); +``` + +Phone OTP is also supported via `challengeWithPhoneNumber`. See the [Passwordless OTP on Database Connections](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-auth-js/EXAMPLES.md#passwordless-otp-on-database-connections) section in EXAMPLES.md for detailed error handling, MFA integration, and tenant prerequisites. + +### 10. More Examples + +A full overview of examples can be found in [EXAMPLES.md](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-auth-js/EXAMPLES.md). + +## Feedback + +### Contributing + +We appreciate feedback and contribution to this repo! Before you get started, please read the following: + +- [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) +- [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) +- [This repo's contribution guide](https://github.com/auth0/auth0-auth-js/blob/main/CONTRIBUTING.md) + +### Raise an issue + +To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-auth-js/issues). + +## Vulnerability Reporting + +Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues. + +## What is Auth0? + +

+ + + + Auth0 Logo + +

+

+ Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0? +

+

+ This project is licensed under the MIT license. See the LICENSE file for more info. +

diff --git a/backend/node_modules/@auth0/auth0-auth-js/package.json b/backend/node_modules/@auth0/auth0-auth-js/package.json new file mode 100644 index 00000000..0f3f30fa --- /dev/null +++ b/backend/node_modules/@auth0/auth0-auth-js/package.json @@ -0,0 +1,61 @@ +{ + "name": "@auth0/auth0-auth-js", + "version": "1.12.0", + "description": "Auth0 Authentication Client for JavaScript runtimes.", + "author": "Auth0", + "license": "MIT", + "type": "module", + "scripts": { + "build": "tsup", + "build:watch": "tsup --watch", + "clean": "rm -rf ./dist", + "lint": "eslint \"./**/*.ts*\"", + "test": "vitest run", + "test:ci": "vitest --watch false --coverage", + "test:workers": "vitest run --config vitest.config.workers.ts" + }, + "types": "./dist/index.d.ts", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.cjs", + "import": "./dist/index.js" + } + }, + "dependencies": { + "jose": "^6.0.8", + "openid-client": "^6.8.0" + }, + "devDependencies": { + "@auth0/typescript-config": "*", + "@eslint/js": "^9.20.0", + "@vitest/coverage-v8": "^3.0.6", + "eslint": "^9.20.1", + "msw": "^2.7.0", + "tsup": "^8.4.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.24.0", + "vitest": "^3.0.5", + "@cloudflare/vitest-pool-workers": "^0.12.10", + "wrangler": "^4.98.0" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git://github.com/auth0/auth0-auth-js.git" + }, + "bugs": { + "url": "https://github.com/auth0/auth0-auth-js/issues" + }, + "homepage": "https://github.com/auth0/auth0-auth-js#readme", + "keywords": [ + "auth0" + ] +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/LICENSE b/backend/node_modules/@auth0/auth0-spa-js/LICENSE new file mode 100644 index 00000000..d865b759 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-spa-js/README.md b/backend/node_modules/@auth0/auth0-spa-js/README.md new file mode 100644 index 00000000..034b3bc5 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/README.md @@ -0,0 +1,189 @@ +![Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE.](https://cdn.auth0.com/website/sdks/banners/spa-js-banner.png) + +[![npm version](https://img.shields.io/npm/v/@auth0/auth0-spa-js?style=flat-square&logo=npm&logoColor=CB3837)](https://www.npmjs.com/package/@auth0/auth0-spa-js) +[![Codecov](https://img.shields.io/codecov/c/github/auth0/auth0-spa-js)](https://codecov.io/gh/auth0/auth0-spa-js) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/auth0/auth0-spa-js) +![Downloads](https://img.shields.io/npm/dw/@auth0/auth0-spa-js) +[![License](https://img.shields.io/:license-mit-blue.svg?style=flat)](https://opensource.org/licenses/MIT) +![CircleCI](https://img.shields.io/circleci/build/github/auth0/auth0-spa-js) + +📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started) - 💻 [API Reference](#api-reference) - 💬 [Feedback](#feedback) + +## Documentation + +- [Quickstart](https://auth0.com/docs/quickstart/spa/vanillajs/interactive) - our interactive guide for quickly adding login, logout and user information to your app using Auth0. +- [Sample app](https://github.com/auth0-samples/auth0-javascript-samples/tree/master/01-Login) - a full-fledged sample app integrated with Auth0. +- [FAQs](https://github.com/auth0/auth0-spa-js/blob/main/FAQ.md) - frequently asked questions about auth0-spa-js SDK. +- [Examples](https://github.com/auth0/auth0-spa-js/blob/main/EXAMPLES.md) - code samples for common scenarios. +- [Docs Site](https://auth0.com/docs) - explore our Docs site and learn more about Auth0. + +## Getting Started + +### Installation + +Using [npm](https://npmjs.org) in your project directory run the following command: + +```sh +npm install @auth0/auth0-spa-js +``` + +From the CDN: + +```html + +``` + +### Configure Auth0 + +Create a **Single Page Application** in the [Auth0 Dashboard](https://manage.auth0.com/#/applications). + +> **If you're using an existing application**, verify that you have configured the following settings in your Single Page Application: +> +> - Click on the "Settings" tab of your application's page. +> - Scroll down and click on the "Show Advanced Settings" link. +> - Under "Advanced Settings", click on the "OAuth" tab. +> - Ensure that "JsonWebToken Signature Algorithm" is set to `RS256` and that "OIDC Conformant" is enabled. + +Next, configure the following URLs for your application under the "Application URIs" section of the "Settings" page: + +- **Allowed Callback URLs**: `http://localhost:3000` +- **Allowed Logout URLs**: `http://localhost:3000` +- **Allowed Web Origins**: `http://localhost:3000` + +> These URLs should reflect the origins that your application is running on. **Allowed Callback URLs** may also include a path, depending on where you're handling the callback (see below). + +Take note of the **Client ID** and **Domain** values under the "Basic Information" section. You'll need these values in the next step. + +### Configure the SDK + +Create an `Auth0Client` instance before rendering or initializing your application. You should only have one instance of the client. + +```js +import { createAuth0Client } from '@auth0/auth0-spa-js'; + +//with async/await +const auth0 = await createAuth0Client({ + domain: '', + clientId: '', + authorizationParams: { + redirect_uri: '' + } +}); + +//or, you can just instantiate the client on its own +import { Auth0Client } from '@auth0/auth0-spa-js'; + +const auth0 = new Auth0Client({ + domain: '', + clientId: '', + authorizationParams: { + redirect_uri: '' + } +}); + +//if you do this, you'll need to check the session yourself +try { + await auth0.getTokenSilently(); +} catch (error) { + if (error.error !== 'login_required') { + throw error; + } +} +``` + +### Logging In + +You can then use login using the `Auth0Client` instance you created: + +```html + +``` + +```js +//redirect to the Universal Login Page +document.getElementById('login').addEventListener('click', async () => { + await auth0.loginWithRedirect(); +}); + +//in your callback route () +window.addEventListener('load', async () => { + const redirectResult = await auth0.handleRedirectCallback(); + //logged in. you can get the user profile like this: + const user = await auth0.getUser(); + console.log(user); +}); +``` + +### Online Access + +> [!NOTE] +> Online Access (Online Refresh Tokens) support via SDKs is currently in Early Access. To request access to this feature, contact your Auth0 representative. + +> [!WARNING] +> Online Refresh Tokens do not currently support resource servers with **Ephemeral Sessions** enabled. Enabling both `allow_online_access` and "Allow for Ephemeral Sessions" on the same resource server results in the authorization server issuing an Online Refresh Token that is then rejected (`invalid_grant`) on the very next refresh. Until Ephemeral Sessions support is added for Online Refresh Tokens, disable "Allow for Ephemeral Sessions" on any resource server used with `refreshTokenMode: RefreshTokenMode.Online`. + +Set `refreshTokenMode` to `RefreshTokenMode.Online` (together with the required `useRefreshTokens: true` and `useDpop: true`) to use **Online Refresh Tokens** — non-rotating refresh tokens bound to the Auth0 session lifetime. The SDK injects the `online_access` scope and routes renewal through the refresh-token grant. + +```js +import { createAuth0Client, RefreshTokenMode } from '@auth0/auth0-spa-js'; + +const auth0 = await createAuth0Client({ + domain: '', + clientId: '', + useRefreshTokens: true, + refreshTokenMode: RefreshTokenMode.Online, + useDpop: true, + authorizationParams: { + redirect_uri: '' + } +}); +``` + +`refreshTokenMode` is a sub-option of `useRefreshTokens`: it defaults to `RefreshTokenMode.Offline` (the rotating refresh tokens described above) and must be set to `RefreshTokenMode.Online` for Online Refresh Tokens. Online mode requires both `useRefreshTokens: true` and `useDpop: true`. See [Online Access](https://github.com/auth0/auth0-spa-js/blob/main/EXAMPLES.md#online-access-online-refresh-tokens) in EXAMPLES.md for the full guide. + +### More Examples + +For comprehensive examples covering various scenarios including logging out, calling APIs, refresh tokens, online access, organizations, passkeys, MFA, DPoP, and more, see the [EXAMPLES.md](https://github.com/auth0/auth0-spa-js/blob/main/EXAMPLES.md) document. + +## API Reference + +Explore API Methods available in auth0-spa-js. + +- [Configuration Options](https://auth0.github.io/auth0-spa-js/interfaces/Auth0ClientOptions.html) + +- [Auth0Client](https://auth0.github.io/auth0-spa-js/classes/Auth0Client.html) +- [createAuth0Client](https://auth0.github.io/auth0-spa-js/functions/createAuth0Client.html) + +## Feedback + +### Contributing + +We appreciate feedback and contribution to this repo! Before you get started, please see the following: + +- [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) +- [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) +- [This repo's contribution guide](https://github.com/auth0/auth0-spa-js/blob/main/CONTRIBUTING.md) + +### Raise an issue + +To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-spa-js/issues). + +### Vulnerability Reporting + +Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. + +## What is Auth0? + +

+ + + + Auth0 Logo + +

+

+ Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0? +

+

+ This project is licensed under the MIT license. See the LICENSE file for more info. +

\ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-spa-js/package.json b/backend/node_modules/@auth0/auth0-spa-js/package.json new file mode 100644 index 00000000..304ec3ca --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/package.json @@ -0,0 +1,129 @@ +{ + "author": "Auth0", + "name": "@auth0/auth0-spa-js", + "description": "Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE", + "license": "MIT", + "version": "2.24.1", + "main": "dist/lib/auth0-spa-js.cjs.js", + "types": "dist/typings/index.d.ts", + "module": "dist/auth0-spa-js.production.esm.js", + "ccu": { + "name": "auth0-spa-js", + "cdn": "https://cdn.auth0.com", + "bucket": "assets.us.auth0.com", + "localPath": "dist", + "mainBundleFile": "auth0-spa-js.production.js", + "digest": { + "hashes": [ + "sha384" + ], + "extensions": [ + ".js" + ] + } + }, + "dependencies": { + "@auth0/auth0-auth-js": "^1.10.0", + "browser-tabs-lock": "^1.3.0", + "dpop": "^2.1.1", + "es-cookie": "~1.3.2" + }, + "scripts": { + "dev": "rimraf dist && rollup -c --watch", + "start": "npm run dev", + "docs": "typedoc --options ./typedoc.js src", + "build": "rimraf dist && rollup -m -c --environment NODE_ENV:production && npm run test:es-check", + "build:stats": "rimraf dist && rollup -m -c --environment NODE_ENV:production --environment WITH_STATS:true && npm run test:es-check && open bundle-stats/index.html", + "lint": "eslint --ext .jsx,.js src/", + "lint:security": "eslint ./src --ext ts --no-eslintrc --config ./.eslintrc.security", + "test": "jest --coverage --silent", + "test:watch": "jest --coverage --watch", + "test:debug": "node --inspect node_modules/.bin/jest --runInBand", + "test:open:integration": "cypress open", + "test:watch:integration": "concurrently --raw npm:dev 'npm:test:open:integration'", + "test:es-check": "npm run test:es-check:es2017 && npm run test:es-check:es2017:module", + "test:es-check:es2017": "es-check es2017 dist/auth0-spa-js.production.js", + "test:es-check:es2017:module": "es-check es2017 dist/auth0-spa-js.production.esm.js --module", + "test:integration:server": "npm run dev", + "test:integration:tests": "wait-on http://localhost:3000/ && cypress run", + "test:integration": "concurrently --raw --kill-others --success first npm:test:integration:server npm:test:integration:tests", + "serve:coverage": "serve coverage/lcov-report -n", + "serve:stats": "serve bundle-stats -n", + "print-bundle-size": "node ./scripts/print-bundle-size.mjs", + "prepack": "npm run build", + "publish:cdn": "ccu --trace" + }, + "devDependencies": { + "@auth0/component-cdn-uploader": "^3.0.2", + "@babel/core": "^7.28.5", + "@babel/preset-env": "^7.28.5", + "@babel/preset-typescript": "^7.28.5", + "@rollup/plugin-babel": "^6.1.0", + "@rollup/plugin-commonjs": "^21.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-replace": "^4.0.0", + "@types/jest": "^28.1.7", + "@typescript-eslint/eslint-plugin-tslint": "^5.33.1", + "@typescript-eslint/parser": "^5.33.1", + "cli-table": "^0.3.6", + "concurrently": "^7.3.0", + "cypress": "^15.18.0", + "es-check": "^7.0.1", + "eslint": "^8.22.0", + "eslint-plugin-security": "^1.5.0", + "fake-indexeddb": "^6.0.1", + "gzip-size": "^7.0.0", + "husky": "^7.0.4", + "idtoken-verifier": "^2.2.2", + "jest": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "jest-fetch-mock": "^3.0.3", + "jest-junit": "^17.0.0", + "jest-localstorage-mock": "^2.4.22", + "jsonwebtoken": "^9.0.0", + "oidc-provider": "^9.6.0", + "prettier": "^2.7.1", + "pretty-quick": "^3.1.2", + "rimraf": "^3.0.2", + "rollup": "^2.78.0", + "rollup-plugin-analyzer": "^4.0.0", + "rollup-plugin-dev": "^1.1.3", + "rollup-plugin-livereload": "^2.0.5", + "rollup-plugin-sourcemaps": "^0.6.3", + "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-typescript2": "^0.37.0", + "rollup-plugin-visualizer": "^5.7.1", + "rollup-plugin-web-worker-loader": "~1.6.1", + "serve": "^14.0.1", + "ts-jest": "^28.0.8", + "tslib": "^2.4.0", + "typedoc": "^0.25.1", + "typescript": "^4.7.4", + "wait-on": "^9.0.0" + }, + "files": [ + "src", + "dist" + ], + "repository": { + "type": "git", + "url": "git://github.com/auth0/auth0-spa-js.git" + }, + "bugs": { + "url": "https://github.com/auth0/auth0-spa-js/issues" + }, + "homepage": "https://github.com/auth0/auth0-spa-js#readme", + "keywords": [ + "auth0", + "login", + "Authorization Code Grant Flow", + "PKCE", + "Single Page Application authentication", + "SPA authentication" + ], + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.ts b/backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.ts new file mode 100644 index 00000000..0d7b5051 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.ts @@ -0,0 +1,2273 @@ +import { + createQueryParams, + runPopup, + parseAuthenticationResult, + encode, + createRandomString, + runIframe, + sha256, + bufferToBase64UrlEncoded, + validateCrypto, + openPopup, + getDomain, + getTokenIssuer, + parseNumber, + stripAuth0Client +} from './utils'; + +import { getLockManager, type ILockManager } from './lock'; + +import { oauthToken, revokeToken } from './api'; + +import { injectDefaultScopes, scopesToRequest } from './scope'; + +import { + InMemoryCache, + ICache, + CacheKey, + CacheManager, + CacheEntry, + IdTokenEntry, + CACHE_KEY_ID_TOKEN_SUFFIX, + DecodedToken +} from './cache'; + +import { ConnectAccountTransaction, LoginTransaction, TransactionManager } from './transaction-manager'; +import { verify as verifyIdToken } from './jwt'; +import { + AuthenticationError, + ConnectError, + GenericError, + InvalidConfigurationError, + MfaRequiredError, + MissingRefreshTokenError, + MissingScopesError, + PopupOpenError, + TimeoutError +} from './errors'; + +import { + ClientStorage, + CookieStorage, + CookieStorageWithLegacySameSite, + SessionStorage +} from './storage'; + +import { + CACHE_LOCATION_MEMORY, + DEFAULT_POPUP_CONFIG_OPTIONS, + DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS, + MISSING_REFRESH_TOKEN_ERROR_MESSAGE, + MFA_STEP_UP_ERROR_DESCRIPTION, + DEFAULT_SCOPE, + ONLINE_ACCESS_SCOPE, + DEFAULT_SESSION_CHECK_EXPIRY_DAYS, + DEFAULT_AUTH0_CLIENT, + INVALID_REFRESH_TOKEN_ERROR_MESSAGE, + USER_BLOCKED_ERROR_MESSAGE, + DEFAULT_NOW_PROVIDER, + DEFAULT_FETCH_TIMEOUT_MS, + DEFAULT_AUDIENCE, + SESSION_EXPIRY_LEEWAY_SECONDS +} from './constants'; + +import { + Auth0ClientOptions, + AuthorizationParams, + AuthorizeOptions, + RedirectLoginOptions, + PopupLoginOptions, + PopupConfigOptions, + RedirectLoginResult, + GetTokenSilentlyOptions, + GetTokenWithPopupOptions, + LogoutOptions, + CacheLocation, + LogoutUrlOptions, + User, + IdToken, + GetTokenSilentlyVerboseResponse, + TokenEndpointResponse, + AuthenticationResult, + ConnectAccountRedirectResult, + RedirectConnectAccountOptions, + ResponseType, + ClientAuthorizationParams, + ClientConfiguration, + RevokeRefreshTokenOptions +} from './global'; + +// @ts-ignore +import TokenWorker from './worker/token.worker.ts'; +import { sendMessage } from './worker/worker.utils'; +import { CacheKeyManifest } from './cache/key-manifest'; +import { + buildIsAuthenticatedCookieName, + buildOrganizationHintCookieName, + cacheFactory, + getAuthorizeParams, + buildGetTokenSilentlyLockKey, + buildIframeLockKey, + OLD_IS_AUTHENTICATED_COOKIE_NAME, + patchOpenUrlWithOnRedirect, + getScopeToRequest, + isRefreshWithMrrt, + getMissingScopes +} from './Auth0Client.utils'; +import { CustomTokenExchangeOptions } from './TokenExchange'; +import { Dpop } from './dpop/dpop'; +import { + Fetcher, + type FetcherConfig, + type CustomFetchMinimalOutput, + type AuthParams +} from './fetcher'; +import { MyAccountApiClient } from './myaccount'; +import { MfaApiClient } from './mfa'; +import { PasskeyApiClient } from './passkey'; +import type { PasskeyCredentialResponse } from './passkey/types'; +import { AuthClient as Auth0AuthJsClient } from '@auth0/auth0-auth-js'; + +/** + * @ignore + */ +type GetTokenSilentlyResult = TokenEndpointResponse & { + decodedToken: ReturnType; + scope: string; + oauthTokenScope?: string; + audience: string; +}; + +/** + * Auth0 SDK for Single Page Applications using [Authorization Code Grant Flow with PKCE](https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce). + */ +export class Auth0Client { + private readonly transactionManager: TransactionManager; + private readonly cacheManager: CacheManager; + private readonly lockManager: ILockManager; + private readonly domainUrl: string; + private readonly tokenIssuer: string; + private readonly scope: Record; + private readonly cookieStorage: ClientStorage; + private readonly dpop: Dpop | undefined; + private readonly sessionCheckExpiryDays: number; + private readonly orgHintCookieName: string; + private readonly isAuthenticatedCookieName: string; + private readonly nowProvider: () => number | Promise; + private readonly httpTimeoutMs: number; + private readonly onlineAccess: boolean; + private readonly options: Auth0ClientOptions & { + authorizationParams: ClientAuthorizationParams, + }; + private readonly userCache: ICache = new InMemoryCache().enclosedCache; + public readonly myAccount: MyAccountApiClient; + + /** + * MFA API client for multi-factor authentication operations. + * + * Provides methods for: + * - Listing enrolled authenticators + * - Enrolling new authenticators (OTP, SMS, Voice, Push, Email) + * - Initiating MFA challenges + * - Verifying MFA challenges + */ + public readonly mfa: MfaApiClient; + + /** + * Passkey API client for passwordless authentication. + * + * Provides two single-call methods that handle the full WebAuthn flow internally: + * - `signup(options)` — register a new user with a passkey + * - `login(options?)` — authenticate an existing user with a passkey + */ + public readonly passkey: PasskeyApiClient; + + private worker?: Worker; + private readonly authJsClient: Auth0AuthJsClient; + + private readonly defaultOptions: Partial = { + authorizationParams: { + scope: DEFAULT_SCOPE + }, + useRefreshTokensFallback: false, + useFormData: true, + refreshTokenMode: 'offline', + }; + + /** Validates online-access config and returns whether online mode is enabled. */ + private resolveOnlineAccess(options: Auth0ClientOptions): boolean { + if (options.refreshTokenMode !== 'online') { + return false; + } + + if (options.useRefreshTokens !== true) { + throw new InvalidConfigurationError( + '`refreshTokenMode: "online"` requires the refresh-token grant.', + 'Set `useRefreshTokens: true`.' + ); + } + + if (options.useDpop !== true) { + throw new InvalidConfigurationError( + '`refreshTokenMode: "online"` requires DPoP, which is missing or disabled.', + 'Set `useDpop: true` (DPoP is mandatory for online access).' + ); + } + + return true; + } + + constructor(options: Auth0ClientOptions) { + this.onlineAccess = this.resolveOnlineAccess(options); + + this.options = { + ...this.defaultOptions, + ...options, + authorizationParams: { + ...this.defaultOptions.authorizationParams, + ...options.authorizationParams + } + }; + + typeof window !== 'undefined' && validateCrypto(); + + this.lockManager = getLockManager(); + + if (options.cache && options.cacheLocation) { + console.warn( + 'Both `cache` and `cacheLocation` options have been specified in the Auth0Client configuration; ignoring `cacheLocation` and using `cache`.' + ); + } + + let cacheLocation: CacheLocation | undefined; + let cache: ICache; + + if (options.cache) { + cache = options.cache; + } else { + cacheLocation = options.cacheLocation || CACHE_LOCATION_MEMORY; + + if (!cacheFactory(cacheLocation)) { + throw new Error(`Invalid cache location "${cacheLocation}"`); + } + + cache = cacheFactory(cacheLocation)(); + } + + this.httpTimeoutMs = options.httpTimeoutInSeconds + ? options.httpTimeoutInSeconds * 1000 + : DEFAULT_FETCH_TIMEOUT_MS; + + this.cookieStorage = + options.legacySameSiteCookie === false + ? CookieStorage + : CookieStorageWithLegacySameSite; + + this.orgHintCookieName = buildOrganizationHintCookieName( + this.options.clientId + ); + + this.isAuthenticatedCookieName = buildIsAuthenticatedCookieName( + this.options.clientId + ); + + this.sessionCheckExpiryDays = + options.sessionCheckExpiryDays || DEFAULT_SESSION_CHECK_EXPIRY_DAYS; + + const transactionStorage = options.useCookiesForTransactions + ? this.cookieStorage + : SessionStorage; + + // `online_access` and `offline_access` are mutually exclusive — inject at most one. + let sessionScope = ''; + if (this.onlineAccess) { + sessionScope = ONLINE_ACCESS_SCOPE; + } else if (this.options.useRefreshTokens) { + sessionScope = 'offline_access'; + } + + this.scope = injectDefaultScopes( + this.options.authorizationParams.scope, + 'openid', + sessionScope + ); + + this.transactionManager = new TransactionManager( + transactionStorage, + this.options.clientId, + this.options.cookieDomain + ); + + this.nowProvider = this.options.nowProvider || DEFAULT_NOW_PROVIDER; + + this.cacheManager = new CacheManager( + cache, + !cache.allKeys + ? new CacheKeyManifest(cache, this.options.clientId) + : undefined, + this.nowProvider + ); + + this.dpop = this.options.useDpop + ? new Dpop(this.options.clientId) + : undefined; + + this.domainUrl = getDomain(this.options.domain); + this.tokenIssuer = getTokenIssuer(this.options.issuer, this.domainUrl); + + const myAccountApiIdentifier = `${this.domainUrl}/me/`; + const myAccountFetcher = this.createFetcher({ + ...(this.options.useDpop && { dpopNonceId: '__auth0_my_account_api__' }), + getAccessToken: (authParams?: AuthParams) => + this.getTokenSilently({ + authorizationParams: { + scope: authParams?.scope?.join(' '), + audience: myAccountApiIdentifier + }, + detailedResponse: true + }) + }); + this.myAccount = new MyAccountApiClient( + myAccountFetcher, + myAccountApiIdentifier + ); + + // Initialize auth-js client foundational Oauth feature support + this.authJsClient = new Auth0AuthJsClient({ + domain: this.options.domain, + clientId: this.options.clientId, + }); + this.mfa = new MfaApiClient(this.authJsClient.mfa, this); + this.passkey = new PasskeyApiClient( + this.authJsClient.passkey, + this + ); + + // Don't use web workers unless using refresh tokens in memory. + if ( + typeof window !== 'undefined' && + window.Worker && + this.options.useRefreshTokens && + cacheLocation === CACHE_LOCATION_MEMORY + ) { + if (this.options.workerUrl) { + this.worker = new Worker(this.options.workerUrl); + } else { + this.worker = new TokenWorker(); + } + + this.worker!.postMessage({ + type: 'init', + allowedBaseUrl: this.domainUrl + }); + } + } + + /** + * Returns a readonly copy of the initialization configuration. + * + * @returns An object containing domain and clientId + * + * @example + * ```typescript + * const auth0 = new Auth0Client({ + * domain: 'tenant.auth0.com', + * clientId: 'abc123' + * }); + * + * const config = auth0.getConfiguration(); + * // { domain: 'tenant.auth0.com', clientId: 'abc123' } + * ``` + */ + public getConfiguration(): Readonly { + return Object.freeze({ + domain: this.options.domain, + clientId: this.options.clientId + }); + } + + private _url(path: string) { + const auth0ClientObj = this.options.auth0Client || DEFAULT_AUTH0_CLIENT; + // Strip env from auth0Client for /authorize to prevent query param truncation + const strippedAuth0Client = stripAuth0Client(auth0ClientObj, true); + const auth0Client = encodeURIComponent( + btoa(JSON.stringify(strippedAuth0Client)) + ); + return `${this.domainUrl}${path}&auth0Client=${auth0Client}`; + } + + private _authorizeUrl(authorizeOptions: AuthorizeOptions) { + return this._url(`/authorize?${createQueryParams(authorizeOptions)}`); + } + + private async _verifyIdToken( + id_token: string, + nonce?: string, + organization?: string + ) { + const now = await this.nowProvider(); + + return verifyIdToken({ + iss: this.tokenIssuer, + aud: this.options.clientId, + id_token, + nonce, + organization, + leeway: this.options.leeway, + max_age: parseNumber(this.options.authorizationParams.max_age), + now + }); + } + + private _processOrgHint(organization?: string) { + if (organization) { + this.cookieStorage.save(this.orgHintCookieName, organization, { + daysUntilExpire: this.sessionCheckExpiryDays, + cookieDomain: this.options.cookieDomain + }); + } else { + this.cookieStorage.remove(this.orgHintCookieName, { + cookieDomain: this.options.cookieDomain + }); + } + } + + /** + * Extracts the session transfer token from the current URL query parameters + * for Native to Web SSO flows. + * + * @param paramName The query parameter name to extract from the URL + * @returns The session transfer token if present, undefined otherwise + */ + private _extractSessionTransferToken(paramName: string): string | undefined { + const params = new URLSearchParams(window.location.search); + return params.get(paramName) || undefined; + } + + /** + * Clears the session transfer token from the current URL using the History API. + * This prevents the token from being re-sent on subsequent authentication requests, + * which is important since session transfer tokens are typically single-use. + * + * @param paramName The query parameter name to remove from the URL + */ + private _clearSessionTransferTokenFromUrl(paramName: string): void { + try { + const url = new URL(window.location.href); + if (url.searchParams.has(paramName)) { + url.searchParams.delete(paramName); + window.history.replaceState({}, '', url.toString()); + } + } catch { + // Silently fail if URL manipulation isn't possible + } + } + + /** + * Applies the session transfer token from the URL to the authorization parameters + * if configured and not already provided. + * + * @param authorizationParams The authorization parameters to enhance + * @returns The authorization parameters with session_transfer_token added if applicable + */ + private _applySessionTransferToken( + authorizationParams: AuthorizationParams + ): AuthorizationParams { + const paramName = this.options.sessionTransferTokenQueryParamName; + if (!paramName || authorizationParams.session_transfer_token) { + return authorizationParams; + } + const token = this._extractSessionTransferToken(paramName); + if (!token) return authorizationParams; + this._clearSessionTransferTokenFromUrl(paramName); + return { ...authorizationParams, session_transfer_token: token }; + } + + private async _prepareAuthorizeUrl( + authorizationParams: AuthorizationParams, + authorizeOptions?: Partial, + fallbackRedirectUri?: string + ): Promise<{ + scope: string; + audience: string; + redirect_uri?: string; + nonce: string; + code_verifier: string; + state: string; + url: string; + }> { + const state = encode(createRandomString()); + const nonce = encode(createRandomString()); + const code_verifier = createRandomString(); + const code_challengeBuffer = await sha256(code_verifier); + const code_challenge = bufferToBase64UrlEncoded(code_challengeBuffer); + const thumbprint = await this.dpop?.calculateThumbprint(); + + const params = getAuthorizeParams( + this.options, + this.scope, + authorizationParams, + state, + nonce, + code_challenge, + authorizationParams.redirect_uri || + this.options.authorizationParams.redirect_uri || + fallbackRedirectUri, + authorizeOptions?.response_mode, + thumbprint + ); + + const url = this._authorizeUrl(params); + + return { + nonce, + code_verifier, + scope: params.scope, + audience: params.audience || DEFAULT_AUDIENCE, + redirect_uri: params.redirect_uri, + state, + url + }; + } + + /** + * ```js + * try { + * await auth0.loginWithPopup(options); + * } catch(e) { + * if (e instanceof PopupCancelledError) { + * // Popup was closed before login completed + * } + * } + * ``` + * + * Opens a popup with the `/authorize` URL using the parameters + * provided as arguments. Random and secure `state` and `nonce` + * parameters will be auto-generated. If the response is successful, + * results will be valid according to their expiration times. + * + * IMPORTANT: This method has to be called from an event handler + * that was started by the user like a button click, for example, + * otherwise the popup will be blocked in most browsers. + * + * @param options + * @param config + */ + public async loginWithPopup( + options?: PopupLoginOptions, + config?: PopupConfigOptions + ) { + options = options || {}; + config = config || {}; + + if (!config.popup) { + config.popup = openPopup(''); + + if (!config.popup) { + throw new PopupOpenError(); + } + } + + const authorizationParams = this._applySessionTransferToken(options.authorizationParams || {}); + + const params = await this._prepareAuthorizeUrl( + authorizationParams, + { response_mode: 'web_message' }, + window.location.origin + ); + + config.popup.location.href = params.url; + + const codeResult = await runPopup( + { + ...config, + timeoutInSeconds: + config.timeoutInSeconds || + this.options.authorizeTimeoutInSeconds || + DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS + }, + new URL(params.url).origin + ); + + if (params.state !== codeResult.state) { + throw new GenericError('state_mismatch', 'Invalid state'); + } + + const organization = + options.authorizationParams?.organization || + this.options.authorizationParams.organization; + + await this._requestToken( + { + audience: params.audience, + scope: params.scope, + code_verifier: params.code_verifier, + grant_type: 'authorization_code', + code: codeResult.code as string, + redirect_uri: params.redirect_uri + }, + { + nonceIn: params.nonce, + organization + } + ); + } + + /** + * ```js + * const user = await auth0.getUser(); + * ``` + * + * Returns the user information if available (decoded + * from the `id_token`). + * + * @typeparam TUser The type to return, has to extend {@link User}. + */ + public async getUser(): Promise { + if (await this._isSessionCeilingReached()) { + return undefined; + } + + const cache = await this._getIdTokenFromCache(); + + return cache?.decodedToken?.user as TUser; + } + + /** + * ```js + * const claims = await auth0.getIdTokenClaims(); + * ``` + * + * Returns all claims from the id_token if available. + */ + public async getIdTokenClaims(): Promise { + if (await this._isSessionCeilingReached()) { + return undefined; + } + + const cache = await this._getIdTokenFromCache(); + + return cache?.decodedToken?.claims; + } + + /** + * ```js + * await auth0.loginWithRedirect(options); + * ``` + * + * Performs a redirect to `/authorize` using the parameters + * provided as arguments. Random and secure `state` and `nonce` + * parameters will be auto-generated. + * + * @param options + */ + public async loginWithRedirect( + options: RedirectLoginOptions = {} + ) { + const { openUrl, fragment, appState, ...urlOptions } = + patchOpenUrlWithOnRedirect(options); + + const organization = + urlOptions.authorizationParams?.organization || + this.options.authorizationParams.organization; + + const authorizationParams = this._applySessionTransferToken(urlOptions.authorizationParams || {}); + + const { url, ...transaction } = await this._prepareAuthorizeUrl( + authorizationParams + ); + + this.transactionManager.create({ + ...transaction, + appState, + response_type: ResponseType.Code, + ...(organization && { organization }) + }); + + const urlWithFragment = fragment ? `${url}#${fragment}` : url; + + if (openUrl) { + await openUrl(urlWithFragment); + } else { + window.location.assign(urlWithFragment); + } + } + + /** + * After the browser redirects back to the callback page, + * call `handleRedirectCallback` to handle success and error + * responses from Auth0. If the response is successful, results + * will be valid according to their expiration times. + */ + public async handleRedirectCallback( + url: string = window.location.href + ): Promise< + RedirectLoginResult | ConnectAccountRedirectResult + > { + const queryStringFragments = url.split('?').slice(1); + + if (queryStringFragments.length === 0) { + throw new Error('There are no query params available for parsing.'); + } + + const transaction = this.transactionManager.get< + LoginTransaction | ConnectAccountTransaction + >(); + + if (!transaction) { + throw new GenericError('missing_transaction', 'Invalid state'); + } + + this.transactionManager.remove(); + + const authenticationResult = parseAuthenticationResult( + queryStringFragments.join('') + ); + + if (transaction.response_type === ResponseType.ConnectCode) { + return this._handleConnectAccountRedirectCallback( + authenticationResult, + transaction + ); + } + return this._handleLoginRedirectCallback( + authenticationResult, + transaction + ); + } + + /** + * Handles the redirect callback from the login flow. + * + * @template AppState - The application state persisted from the /authorize redirect. + * @param {string} authenticationResult - The parsed authentication result from the URL. + * @param {string} transaction - The login transaction. + * + * @returns {RedirectLoginResult} Resolves with the persisted app state. + * @throws {GenericError | Error} If the transaction is missing, invalid, or the code exchange fails. + */ + private async _handleLoginRedirectCallback( + authenticationResult: AuthenticationResult, + transaction: LoginTransaction + ): Promise> { + const { code, state, error, error_description } = authenticationResult; + + if (error) { + throw new AuthenticationError( + error, + error_description || error, + state, + transaction.appState + ); + } + + // Transaction should have a `code_verifier` to do PKCE for CSRF protection + if ( + !transaction.code_verifier || + (transaction.state && transaction.state !== state) + ) { + throw new GenericError('state_mismatch', 'Invalid state'); + } + + const organization = transaction.organization; + const nonceIn = transaction.nonce; + const redirect_uri = transaction.redirect_uri; + + await this._requestToken( + { + audience: transaction.audience, + scope: transaction.scope, + code_verifier: transaction.code_verifier, + grant_type: 'authorization_code', + code: code as string, + ...(redirect_uri ? { redirect_uri } : {}) + }, + { nonceIn, organization } + ); + + return { + appState: transaction.appState, + response_type: ResponseType.Code + }; + } + + /** + * Handles the redirect callback from the connect account flow. + * This works the same as the redirect from the login flow expect it verifies the `connect_code` + * with the My Account API rather than the `code` with the Authorization Server. + * + * @template AppState - The application state persisted from the connect redirect. + * @param {string} connectResult - The parsed connect accounts result from the URL. + * @param {string} transaction - The login transaction. + * @returns {Promise} The result of the My Account API, including any persisted app state. + * @throws {GenericError | MyAccountApiError} If the transaction is missing, invalid, or an error is returned from the My Account API. + */ + private async _handleConnectAccountRedirectCallback( + connectResult: AuthenticationResult, + transaction: ConnectAccountTransaction + ): Promise> { + const { connect_code, state, error, error_description } = connectResult; + + if (error) { + throw new ConnectError( + error, + error_description || error, + transaction.connection, + state, + transaction.appState + ); + } + + if (!connect_code) { + throw new GenericError('missing_connect_code', 'Missing connect code'); + } + + if ( + !transaction.code_verifier || + !transaction.state || + !transaction.auth_session || + !transaction.redirect_uri || + transaction.state !== state + ) { + throw new GenericError('state_mismatch', 'Invalid state'); + } + + const data = await this.myAccount.completeAccount({ + auth_session: transaction.auth_session, + connect_code, + redirect_uri: transaction.redirect_uri, + code_verifier: transaction.code_verifier + }); + + return { + ...data, + appState: transaction.appState, + response_type: ResponseType.ConnectCode, + }; + } + + /** + * ```js + * await auth0.checkSession(); + * ``` + * + * Check if the user is logged in using `getTokenSilently`. The difference + * with `getTokenSilently` is that this doesn't return a token, but it will + * pre-fill the token cache. + * + * This method also heeds the `auth0.{clientId}.is.authenticated` cookie, as an optimization + * to prevent calling Auth0 unnecessarily. If the cookie is not present because + * there was no previous login (or it has expired) then tokens will not be refreshed. + * + * It should be used for silently logging in the user when you instantiate the + * `Auth0Client` constructor. You should not need this if you are using the + * `createAuth0Client` factory. + * + * **Note:** the cookie **may not** be present if running an app using a private tab, as some + * browsers clear JS cookie data and local storage when the tab or page is closed, or on page reload. This effectively + * means that `checkSession` could silently return without authenticating the user on page refresh when + * using a private tab, despite having previously logged in. As a workaround, use `getTokenSilently` instead + * and handle the possible `login_required` error [as shown in the readme](https://github.com/auth0/auth0-spa-js#creating-the-client). + * + * @param options + */ + public async checkSession(options?: GetTokenSilentlyOptions) { + if (!this.cookieStorage.get(this.isAuthenticatedCookieName)) { + if (!this.cookieStorage.get(OLD_IS_AUTHENTICATED_COOKIE_NAME)) { + return; + } else { + // Migrate the existing cookie to the new name scoped by client ID + this.cookieStorage.save(this.isAuthenticatedCookieName, true, { + daysUntilExpire: this.sessionCheckExpiryDays, + cookieDomain: this.options.cookieDomain + }); + + this.cookieStorage.remove(OLD_IS_AUTHENTICATED_COOKIE_NAME); + } + } + + try { + await this.getTokenSilently(options); + } catch (_) { } + } + + /** + * Fetches a new access token and returns the response from the /oauth/token endpoint, omitting the refresh token. + * + * @param options + */ + public async getTokenSilently( + options: GetTokenSilentlyOptions & { detailedResponse: true } + ): Promise; + + /** + * Fetches a new access token and returns it. + * + * @param options + */ + public async getTokenSilently( + options?: GetTokenSilentlyOptions + ): Promise; + + /** + * Fetches a new access token, and either returns just the access token (the default) or the response from the /oauth/token endpoint, depending on the `detailedResponse` option. + * + * ```js + * const token = await auth0.getTokenSilently(options); + * ``` + * + * If there's a valid token stored and it has more than 60 seconds + * remaining before expiration, return the token. Otherwise, attempt + * to obtain a new token. + * + * A new token will be obtained either by opening an iframe or a + * refresh token (if `useRefreshTokens` is `true`). + + * If iframes are used, opens an iframe with the `/authorize` URL + * using the parameters provided as arguments. Random and secure `state` + * and `nonce` parameters will be auto-generated. If the response is successful, + * results will be validated according to their expiration times. + * + * If refresh tokens are used, the token endpoint is called directly with the + * 'refresh_token' grant. If no refresh token is available to make this call, + * the SDK will only fall back to using an iframe to the '/authorize' URL if + * the `useRefreshTokensFallback` setting has been set to `true`. By default this + * setting is `false`. + * + * This method may use a web worker to perform the token call if the in-memory + * cache is used. + * + * If an `audience` value is given to this function, the SDK always falls + * back to using an iframe to make the token exchange. + * + * Note that in all cases, falling back to an iframe requires access to + * the `auth0` cookie. + * + * @param options + */ + public async getTokenSilently( + options: GetTokenSilentlyOptions = {} + ): Promise { + const localOptions: GetTokenSilentlyOptions & { + authorizationParams: AuthorizationParams & { scope: string }; + } = { + cacheMode: 'on', + ...options, + authorizationParams: { + ...this.options.authorizationParams, + ...options.authorizationParams, + scope: scopesToRequest( + this.scope, + options.authorizationParams?.scope, + options.authorizationParams?.audience || this.options.authorizationParams.audience, + ) + } + }; + + const result = await this._getTokenSilently(localOptions); + + return options.detailedResponse ? result : result?.access_token; + } + + private async _getTokenSilently( + options: GetTokenSilentlyOptions & { + authorizationParams: AuthorizationParams & { scope: string }; + } + ): Promise { + const { cacheMode, ...getTokenOptions } = options; + + if (await this._isSessionCeilingReached()) { + return undefined; + } + + // Check the cache before acquiring the lock to avoid the latency of + // `lock.acquireLock` when the cache is populated. + if (cacheMode !== 'off') { + const entry = await this._getEntryFromCache({ + scope: getTokenOptions.authorizationParams.scope, + audience: getTokenOptions.authorizationParams.audience || DEFAULT_AUDIENCE, + clientId: this.options.clientId, + cacheMode, + }); + + if (entry) { + return entry; + } + } + + if (cacheMode === 'cache-only') { + return; + } + + // Generate lock key based on client ID and audience for better isolation + const lockKey = buildGetTokenSilentlyLockKey( + this.options.clientId, + getTokenOptions.authorizationParams.audience || 'default' + ); + + try { + return await this.lockManager.runWithLock(lockKey, 5000, async () => { + // Check the cache a second time, because it may have been populated + // by a previous call while this call was waiting to acquire the lock. + if (cacheMode !== 'off') { + const entry = await this._getEntryFromCache({ + scope: getTokenOptions.authorizationParams.scope, + audience: + getTokenOptions.authorizationParams.audience || DEFAULT_AUDIENCE, + clientId: this.options.clientId + }); + + if (entry) { + return entry; + } + } + + const authResult = this.options.useRefreshTokens + ? await this._getTokenUsingRefreshToken(getTokenOptions) + : await this._getTokenFromIFrame(getTokenOptions); + + const { id_token, token_type, access_token, oauthTokenScope, expires_in } = + authResult; + + return { + id_token, + token_type, + access_token, + ...(oauthTokenScope ? { scope: oauthTokenScope } : null), + expires_in + }; + }); + } catch (error) { + // Lock is already released - safe to open popup + if (this._isInteractiveError(error) && this.options.interactiveErrorHandler === 'popup') { + return await this._handleInteractiveErrorWithPopup(getTokenOptions); + } + throw error; + } + } + + /** + * Checks if an error should be handled by the interactive error handler. + * Matches: + * - MfaRequiredError (refresh token path, error='mfa_required') + * - GenericError from iframe path (error='login_required', + * error_description='Multifactor authentication required') + * Extensible for future interactive error types. + */ + private _isInteractiveError( + error: unknown + ): error is MfaRequiredError | GenericError { + return error instanceof MfaRequiredError || (error instanceof GenericError && this._isIframeMfaError(error)); + } + + /** + * Checks if a login_required error from the iframe flow is actually + * an MFA step-up requirement. The /authorize endpoint returns + * error='login_required' with error_description='Multifactor authentication required' + * when MFA is needed but prompt=none prevents interaction. + */ + private _isIframeMfaError(error: GenericError): boolean { + return ( + error.error === 'login_required' && + error.error_description === MFA_STEP_UP_ERROR_DESCRIPTION + ); + } + + /** + * Handles MFA errors by opening a popup to complete authentication, + * then reads the resulting token from cache. + */ + private async _handleInteractiveErrorWithPopup( + options: GetTokenSilentlyOptions & { + authorizationParams: AuthorizationParams & { scope: string }; + } + ): Promise { + try { + await this.loginWithPopup({ + authorizationParams: options.authorizationParams + }); + + const entry = await this._getEntryFromCache({ + scope: options.authorizationParams.scope, + audience: + options.authorizationParams.audience || DEFAULT_AUDIENCE, + clientId: this.options.clientId + }); + + if (!entry) { + throw new GenericError( + 'interactive_handler_cache_miss', + 'Token not found in cache after interactive authentication' + ); + } + + return entry; + } catch (error) { + // Expected errors (all GenericError subclasses): + // - PopupCancelledError: user closed the popup before completing login + // - PopupTimeoutError: popup did not complete within the allowed time + // - PopupOpenError: popup could not be opened (e.g. blocked by browser) + // - GenericError: authentication or cache miss errors + throw error; + } + } + + /** + * ```js + * const token = await auth0.getTokenWithPopup(options); + * ``` + * Opens a popup with the `/authorize` URL using the parameters + * provided as arguments. Random and secure `state` and `nonce` + * parameters will be auto-generated. If the response is successful, + * results will be valid according to their expiration times. + * + * @param options + * @param config + */ + public async getTokenWithPopup( + options: GetTokenWithPopupOptions = {}, + config: PopupConfigOptions = {} + ) { + const localOptions = { + ...options, + authorizationParams: { + ...this.options.authorizationParams, + ...options.authorizationParams, + scope: scopesToRequest( + this.scope, + options.authorizationParams?.scope, + options.authorizationParams?.audience || this.options.authorizationParams.audience + ) + } + }; + + config = { + ...DEFAULT_POPUP_CONFIG_OPTIONS, + ...config + }; + + await this.loginWithPopup(localOptions, config); + + const cache = await this.cacheManager.get( + new CacheKey({ + scope: localOptions.authorizationParams.scope, + audience: localOptions.authorizationParams.audience || DEFAULT_AUDIENCE, + clientId: this.options.clientId + }), + undefined, + this.options.useMrrt + ); + + return cache!.access_token; + } + + /** + * ```js + * const isAuthenticated = await auth0.isAuthenticated(); + * ``` + * + * Returns `true` if there's valid information stored, + * otherwise returns `false`. + * + */ + public async isAuthenticated() { + const user = await this.getUser(); + return !!user; + } + + /** + * ```js + * await auth0.buildLogoutUrl(options); + * ``` + * + * Builds a URL to the logout endpoint using the parameters provided as arguments. + * @param options + */ + private _buildLogoutUrl(options: LogoutUrlOptions): string { + if (options.clientId !== null) { + options.clientId = options.clientId || this.options.clientId; + } else { + delete options.clientId; + } + + const { federated, ...logoutOptions } = options.logoutParams || {}; + const federatedQuery = federated ? `&federated` : ''; + const url = this._url( + `/v2/logout?${createQueryParams({ + clientId: options.clientId, + ...logoutOptions + })}` + ); + + return url + federatedQuery; + } + + /** + * ```js + * await auth0.revokeRefreshToken(); + * ``` + * + * Revokes the refresh token using the `/oauth/revoke` endpoint. + * This invalidates the refresh token so it can no longer be used to obtain new access tokens. + * + * The method works with both memory and localStorage cache modes: + * - For memory storage with worker: The refresh token never leaves the worker thread, + * maintaining security isolation + * - For localStorage: The token is retrieved from cache and revoked + * + * If `useRefreshTokens` is disabled, this method does nothing. + * + * **Online mode:** when `refreshTokenMode` is `'online'`, revoking the ORT via + * `/oauth/revoke` **also terminates the Auth0 session** and **clears the entire local + * cache** (access token, ID token, user profile). Because Online Refresh Tokens are + * session-bound, the authorization server ties the token directly to the session — + * revoking the ORT invalidates the session server-side. The local cache is cleared + * immediately so that `isAuthenticated()` returns `false` and `getUser()` returns + * `undefined` right away, without waiting for the access token to expire. + * Use this when you need to force a sign-out without a redirect (e.g. background + * revocation). For a redirect-based sign-out, prefer `logout()`. + * + * **Important:** This method revokes the refresh token for a single audience. If your + * application requests tokens for multiple audiences, each audience may have its own + * refresh token. To fully revoke all refresh tokens, call this method once per audience. + * If you want to terminate the user's session with a redirect, use `logout()` instead. + * + * When using Multi-Resource Refresh Tokens (MRRT), a single refresh token may cover + * multiple audiences. In that case, revoking it will affect all cache entries that + * share the same token. + * + * @param options - Optional parameters to identify which refresh token to revoke. + * Defaults to the audience configured in `authorizationParams`. + * + * @example + * // Revoke the default refresh token + * await auth0.revokeRefreshToken(); + * + * @example + * // Revoke refresh tokens for each audience individually + * await auth0.revokeRefreshToken({ audience: 'https://api.example.com' }); + * await auth0.revokeRefreshToken({ audience: 'https://api2.example.com' }); + */ + public async revokeRefreshToken(options: RevokeRefreshTokenOptions = {}): Promise { + if (!this.options.useRefreshTokens) { + return; + } + + const audience = + options.audience || this.options.authorizationParams.audience; + + const resolvedAudience = audience || DEFAULT_AUDIENCE; + + // For the non-worker path the main-thread cache holds the refresh tokens. + // For the worker path the worker holds its own RT store — the cache returns + // [] and revokeToken sends a single message; the worker loops internally. + const refreshTokens = await this.cacheManager.getRefreshTokensByAudience( + resolvedAudience, + this.options.clientId + ); + + await revokeToken( + { + baseUrl: this.domainUrl, + timeout: this.httpTimeoutMs, + auth0Client: this.options.auth0Client, + useFormData: this.options.useFormData, + client_id: this.options.clientId, + refreshTokens, + audience: resolvedAudience, + onRefreshTokenRevoked: refreshToken => + this.cacheManager.stripRefreshToken(refreshToken) + }, + this.worker + ); + + // In online mode the ORT is session-bound: revoking it terminates the Auth0 session + // server-side. Clear the entire local cache so that isAuthenticated() returns false + // and getUser() returns undefined immediately — the stale access token and ID token + // must not remain visible to the application after the session is gone. + if (this.onlineAccess) { + await this._clearLocalSession(); + } + } + + /** + * ```js + * await auth0.logout(options); + * ``` + * + * Clears the application session and performs a redirect to `/v2/logout`, using + * the parameters provided as arguments, to clear the Auth0 session. + * + * If the `federated` option is specified it also clears the Identity Provider session. + * [Read more about how Logout works at Auth0](https://auth0.com/docs/logout). + * + * @param options + */ + public async logout(options: LogoutOptions = {}): Promise { + const { openUrl, ...logoutOptions } = patchOpenUrlWithOnRedirect(options); + + await this._clearLocalSession(options.clientId); + + const url = this._buildLogoutUrl(logoutOptions); + + if (openUrl) { + await openUrl(url); + } else if (openUrl !== false) { + window.location.assign(url); + } + } + + private async _getTokenFromIFrame( + options: GetTokenSilentlyOptions & { + authorizationParams: AuthorizationParams & { scope: string }; + } + ): Promise { + const iframeLockKey = buildIframeLockKey(this.options.clientId); + + // Acquire global iframe lock to serialize iframe authorization flows. + // This is necessary because the SDK does not support multiple simultaneous transactions. + // Since https://github.com/auth0/auth0-spa-js/pull/1408, when calling + // `getTokenSilently()`, the global locking will lock per `audience` instead of locking + // only per `client_id`. + // This means that calls for different audiences would happen in parallel, which does + // not work when using silent authentication (prompt=none) from within the SDK, as that + // relies on the same transaction context as a top-level `loginWithRedirect`. + // To resolve that, we add a second-level locking that locks only the iframe calls in + // the same way as was done before https://github.com/auth0/auth0-spa-js/pull/1408. + try { + return await this.lockManager.runWithLock( + iframeLockKey, + 5000, + async () => { + const params: AuthorizationParams & { scope: string } = { + ...options.authorizationParams, + prompt: 'none' + }; + + const orgHint = this.cookieStorage.get( + this.orgHintCookieName + ); + + if (orgHint && !params.organization) { + params.organization = orgHint; + } + + const { + url, + state: stateIn, + nonce: nonceIn, + code_verifier, + redirect_uri, + scope, + audience + } = await this._prepareAuthorizeUrl( + params, + { response_mode: 'web_message' }, + window.location.origin + ); + + // When a browser is running in a Cross-Origin Isolated context, using iframes is not possible. + // It doesn't throw an error but times out instead, so we should exit early and inform the user about the reason. + // https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated + if ((window as any).crossOriginIsolated) { + throw new GenericError( + 'login_required', + 'The application is running in a Cross-Origin Isolated context, silently retrieving a token without refresh token is not possible.' + ); + } + + const authorizeTimeout = + options.timeoutInSeconds || this.options.authorizeTimeoutInSeconds; + + // Extract origin from domainUrl, fallback to domainUrl if URL parsing fails + let eventOrigin: string; + try { + eventOrigin = new URL(this.domainUrl).origin; + } catch { + eventOrigin = this.domainUrl; + } + + const codeResult = await runIframe( + url, + eventOrigin, + authorizeTimeout + ); + + if (stateIn !== codeResult.state) { + throw new GenericError('state_mismatch', 'Invalid state'); + } + + const tokenResult = await this._requestToken( + { + ...options.authorizationParams, + code_verifier, + code: codeResult.code as string, + grant_type: 'authorization_code', + redirect_uri, + timeout: options.authorizationParams.timeout || this.httpTimeoutMs + }, + { + nonceIn, + organization: params.organization + } + ); + + return { + ...tokenResult, + scope: scope, + oauthTokenScope: tokenResult.scope, + audience: audience + }; + } + ); + } catch (e) { + if (e.error === 'login_required') { + // When the login_required error is actually an MFA step-up requirement + // and the interactive error handler is configured, skip logout so the + // session is preserved for the popup flow. + const shouldSkipLogoutForMfaStepUp = + e instanceof GenericError && + this._isIframeMfaError(e) && + this.options.interactiveErrorHandler === 'popup'; + + if (!shouldSkipLogoutForMfaStepUp) { + this.logout({ + openUrl: false + }); + } + } + throw e; + } + } + + private async _getTokenUsingRefreshToken( + options: GetTokenSilentlyOptions & { + authorizationParams: AuthorizationParams & { scope: string }; + } + ): Promise { + const cache = await this.cacheManager.get( + new CacheKey({ + scope: options.authorizationParams.scope, + audience: options.authorizationParams.audience || DEFAULT_AUDIENCE, + clientId: this.options.clientId + }), + undefined, + this.options.useMrrt + ); + + // If you don't have a refresh token in memory + // and you don't have a refresh token in web worker memory + // and useRefreshTokensFallback was explicitly enabled + // fallback to an iframe + if ((!cache || !cache.refresh_token) && !this.worker) { + if (this.options.useRefreshTokensFallback) { + return await this._getTokenFromIFrame(options); + } + + throw new MissingRefreshTokenError( + options.authorizationParams.audience || DEFAULT_AUDIENCE, + options.authorizationParams.scope + ); + } + + const redirect_uri = + options.authorizationParams.redirect_uri || + this.options.authorizationParams.redirect_uri || + window.location.origin; + + const timeout = + typeof options.timeoutInSeconds === 'number' + ? options.timeoutInSeconds * 1000 + : null; + + const scopesToRequest = getScopeToRequest( + this.options.useMrrt, + options.authorizationParams, + cache?.audience, + cache?.scope, + ); + + try { + const tokenResult = await this._requestToken({ + ...options.authorizationParams, + grant_type: 'refresh_token', + refresh_token: cache && cache.refresh_token, + redirect_uri, + ...(timeout && { timeout }) + }, + { + scopesToRequest, + } + ); + + // Propagate a rotated RT to all MRRT entries. Skipped for online mode, + // where ORTs are non-rotating. + if ( + !this.onlineAccess && + tokenResult.refresh_token && + cache?.refresh_token + ) { + await this.cacheManager.updateEntry( + cache.refresh_token, + tokenResult.refresh_token, + this.options.clientId, + this.options.useMrrt + ); + } + + // Some scopes requested to the server might not be inside the refresh policies + // In order to return a token with all requested scopes when using MRRT we should + // check if all scopes are returned. If not, we will try to use an iframe to request + // a token. + if (this.options.useMrrt) { + const isRefreshMrrt = isRefreshWithMrrt( + cache?.audience, + cache?.scope, + options.authorizationParams.audience, + options.authorizationParams.scope, + ); + + if (isRefreshMrrt) { + const missingScopes = getMissingScopes( + scopesToRequest, + tokenResult.scope, + this.onlineAccess + ); + + if (missingScopes) { + if (this.options.useRefreshTokensFallback) { + return await this._getTokenFromIFrame(options); + } + + // Before throwing MissingScopesError, we have to remove the previously created entry + // to avoid storing wrong data + await this.cacheManager.remove( + this.options.clientId, + options.authorizationParams.audience, + options.authorizationParams.scope, + ); + + throw new MissingScopesError( + options.authorizationParams.audience || 'default', + missingScopes, + ); + } + } + } + + return { + ...tokenResult, + scope: options.authorizationParams.scope, + oauthTokenScope: tokenResult.scope, + audience: options.authorizationParams.audience || DEFAULT_AUDIENCE + }; + } catch (e) { + if (e.message) { + // Blocked users should be logged out immediately. No point attempting + // iframe fallback as the authorization server will reject the request. + if (e.message.includes(USER_BLOCKED_ERROR_MESSAGE)) { + await this.logout({ openUrl: false }); + throw e; + } + + // For missing or invalid refresh tokens, attempt iframe fallback if configured. + // The iframe may succeed if the user still has a valid session. + if ( + (e.message.includes(MISSING_REFRESH_TOKEN_ERROR_MESSAGE) || + e.message.includes(INVALID_REFRESH_TOKEN_ERROR_MESSAGE)) && + this.options.useRefreshTokensFallback + ) { + return await this._getTokenFromIFrame(options); + } + } + + throw e; + } + } + + private async _saveEntryInCache( + entry: CacheEntry & { id_token: string; decodedToken: DecodedToken } + ) { + const { session_expiry, iat } = entry.decodedToken.claims; + if (session_expiry !== undefined) { + if (typeof session_expiry !== 'number') { + throw new GenericError( + 'invalid_token', + 'Invalid session_expiry: value must be a number.' + ); + } + if (session_expiry >= 10_000_000_000) { + throw new GenericError( + 'invalid_token', + 'Invalid session_expiry: value appears to be in milliseconds; expected a Unix timestamp in seconds.' + ); + } + if (iat === undefined || session_expiry <= iat) { + throw new GenericError( + 'invalid_token', + 'Invalid session_expiry: session ceiling is before or at the token issue time.' + ); + } + } + + const { id_token, decodedToken, ...entryWithoutIdToken } = entry; + + this.userCache.set(CACHE_KEY_ID_TOKEN_SUFFIX, { + id_token, + decodedToken + }); + + await this.cacheManager.setIdToken( + this.options.clientId, + entry.id_token, + entry.decodedToken + ); + + await this.cacheManager.set(entryWithoutIdToken); + } + + private async _clearLocalSession(clientId: string | null | undefined = this.options.clientId) { + if (clientId === null) { + await this.cacheManager.clear(); + } else { + await this.cacheManager.clear(clientId); + } + this.cookieStorage.remove(this.orgHintCookieName, { + cookieDomain: this.options.cookieDomain + }); + this.cookieStorage.remove(this.isAuthenticatedCookieName, { + cookieDomain: this.options.cookieDomain + }); + this.userCache.remove(CACHE_KEY_ID_TOKEN_SUFFIX); + + try { + await this.dpop?.clear(); + } catch { + // DPoP storage cleanup is best-effort. + } + + if (this.worker) { + try { + await sendMessage({ type: 'clear' }, this.worker); + } catch { + // Worker cleanup is best-effort. + } + } + } + + private async _isSessionCeilingReached(): Promise { + const inMemory = this.userCache.get( + CACHE_KEY_ID_TOKEN_SUFFIX + ) as IdTokenEntry; + const idTokenEntry = + inMemory ?? + await this.cacheManager.getIdToken( + new CacheKey({ clientId: this.options.clientId }) + ); + const sessionExpiresAt = idTokenEntry?.decodedToken?.claims?.session_expiry; + if (sessionExpiresAt === undefined) return false; + + const now = await this.nowProvider(); + const nowSeconds = Math.floor(now / 1000); + if (nowSeconds >= sessionExpiresAt - SESSION_EXPIRY_LEEWAY_SECONDS) { + await this._clearLocalSession(); + return true; + } + return false; + } + + private async _getIdTokenFromCache() { + const audience = this.options.authorizationParams.audience || DEFAULT_AUDIENCE; + const scope = this.scope[audience]; + + const cache = await this.cacheManager.getIdToken( + new CacheKey({ + clientId: this.options.clientId, + audience, + scope, + }) + ); + + const currentCache = this.userCache.get( + CACHE_KEY_ID_TOKEN_SUFFIX + ) as IdTokenEntry; + + // If the id_token in the cache matches the value we previously cached in memory return the in-memory + // value so that object comparison will work + if (cache && cache.id_token === currentCache?.id_token) { + return currentCache; + } + + this.userCache.set(CACHE_KEY_ID_TOKEN_SUFFIX, cache); + return cache; + } + + private async _getEntryFromCache({ + scope, + audience, + clientId, + cacheMode, + }: { + scope: string; + audience: string; + clientId: string; + cacheMode?: string; + }): Promise { + const entry = await this.cacheManager.get( + new CacheKey({ + scope, + audience, + clientId + }), + 60, // get a new token if within 60 seconds of expiring + this.options.useMrrt, + cacheMode, + ); + + if (entry && entry.access_token) { + const { token_type, access_token, oauthTokenScope, expires_in } = + entry as CacheEntry; + const cache = await this._getIdTokenFromCache(); + return ( + cache && { + id_token: cache.id_token, + token_type: token_type ? token_type : 'Bearer', + access_token, + ...(oauthTokenScope ? { scope: oauthTokenScope } : null), + expires_in + } + ); + } + } + + // Central MFA context store — called from _requestToken for all non-authorization_code + // grants (including via _requestTokenForMfa) and from customTokenExchange. + private _storeMfaContext(e: unknown, scope?: string, audience?: string): void { + if (e instanceof MfaRequiredError) { + this.mfa.setMFAAuthDetails(e.mfa_token, scope, audience, e.mfa_requirements); + } + } + + private async _requestToken( + options: + | PKCERequestTokenOptions + | RefreshTokenRequestTokenOptions + | TokenExchangeRequestOptions + | WebauthnRequestTokenOptions, + additionalParameters?: RequestTokenAdditionalParameters + ) { + const { nonceIn, organization, scopesToRequest } = additionalParameters || {}; + try { + const authResult = await oauthToken( + { + baseUrl: this.domainUrl, + client_id: this.options.clientId, + auth0Client: this.options.auth0Client, + useFormData: this.options.useFormData, + timeout: this.httpTimeoutMs, + useMrrt: this.options.useMrrt, + dpop: this.dpop, + preserveRefreshToken: this.onlineAccess, + ...options, + scope: scopesToRequest || options.scope, + }, + this.worker + ); + + let decodedToken = await this._verifyIdToken( + authResult.id_token, + nonceIn, + organization + ); + + // When logging in with authorization_code, check if a different user is authenticating + // If so, clear the cache to prevent tokens from multiple users coexisting + if (options.grant_type === 'authorization_code') { + const existingIdToken = await this._getIdTokenFromCache(); + + if (existingIdToken?.decodedToken?.claims?.sub && + existingIdToken.decodedToken.claims.sub !== decodedToken.claims.sub) { + // Different user detected - clear cached tokens + await this.cacheManager.clear(this.options.clientId); + this.userCache.remove(CACHE_KEY_ID_TOKEN_SUFFIX); + } + } + + // Silent refresh — pin the session_expiry ceiling from the initial login. + // Prevents the server from extending the ceiling via a refresh token response. + if (options.grant_type !== 'authorization_code') { + const existingIdToken = await this._getIdTokenFromCache(); + const existingCeiling = existingIdToken?.decodedToken?.claims?.session_expiry; + if (existingCeiling !== undefined) { + decodedToken = { + ...decodedToken, + claims: { ...decodedToken.claims, session_expiry: existingCeiling } + }; + } + } + // Online refresh tokens are non-rotating: the server returns no refresh_token, so + // carry the ORT forward to avoid evicting it on save. + // Online-only: in offline mode a refresh token is single-use and reusing it would + // trip reuse detection. + // Mutate authResult so ...authResult in _saveEntryInCache picks up the ORT directly. + // Safe: GetTokenSilentlyVerboseResponse omits refresh_token, so it never reaches app code. + if (!authResult.refresh_token && this.onlineAccess) { + authResult.refresh_token = (options as RefreshTokenRequestTokenOptions).refresh_token ?? (await this.cacheManager.get( + new CacheKey({ + scope: scopesToRequest || options.scope, + audience: options.audience || DEFAULT_AUDIENCE, + clientId: this.options.clientId, + }), + undefined, + this.options.useMrrt + ))?.refresh_token; + } + + await this._saveEntryInCache({ + ...authResult, + decodedToken, + scope: options.scope, + audience: options.audience || DEFAULT_AUDIENCE, + ...(authResult.scope ? { oauthTokenScope: authResult.scope } : null), + client_id: this.options.clientId + }); + + this.cookieStorage.save(this.isAuthenticatedCookieName, true, { + daysUntilExpire: this.sessionCheckExpiryDays, + cookieDomain: this.options.cookieDomain + }); + + this._processOrgHint(organization || decodedToken.claims.org_id); + + return { ...authResult, decodedToken }; + } catch (e) { + // Exclude authorization_code: MFA is handled by Universal Login during the + // authorization phase for redirect/popup flows, not via a token endpoint error. + if (options.grant_type !== 'authorization_code') { + this._storeMfaContext(e, scopesToRequest || options.scope, options.audience); + } + throw e; + } + } + + /* + Custom Token Exchange + * **Implementation Notes:** + * - Ensure that the `subject_token` provided has been securely obtained and is valid according + * to your external identity provider's policies before invoking this function. + * - The function leverages internal helper methods: + * - `validateTokenType` confirms that the `subject_token_type` is supported. + * - `getUniqueScopes` merges and de-duplicates scopes between the provided options and + * the instance's default scopes. + * - `_requestToken` performs the actual HTTP request to the token endpoint. + */ + + /** + * ```js + * await auth0.loginWithCustomTokenExchange(options); + * ``` + * + * Exchanges an external subject token for Auth0 tokens and logs the user in. + * This method implements the Custom Token Exchange grant as specified in RFC 8693. + * + * The exchanged tokens are automatically cached, establishing an authenticated session. + * After calling this method, you can use `getUser()`, `getIdTokenClaims()`, and + * `getTokenSilently()` to access the user's information and tokens. + * + * @param {CustomTokenExchangeOptions} options - The options required to perform the token exchange. + * + * @returns {Promise} A promise that resolves to the token endpoint response, + * which contains the issued Auth0 tokens (access_token, id_token, etc.). + * + * The request includes the following parameters: + * - `grant_type`: "urn:ietf:params:oauth:grant-type:token-exchange" + * - `subject_token`: The external token to exchange + * - `subject_token_type`: The type identifier of the external token + * - `scope`: Merged scopes from the request and SDK defaults + * - `audience`: Target audience (defaults to SDK configuration) + * - `organization`: Optional organization ID/name for org-scoped authentication + * + * **Example Usage:** + * + * ```js + * const options = { + * subject_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...', + * subject_token_type: 'urn:acme:legacy-system-token', + * scope: 'openid profile email', + * audience: 'https://api.example.com', + * organization: 'org_12345' + * }; + * + * try { + * const tokenResponse = await auth0.loginWithCustomTokenExchange(options); + * console.log('Access token:', tokenResponse.access_token); + * + * // User is now logged in - access user info + * const user = await auth0.getUser(); + * console.log('Logged in user:', user); + * } catch (error) { + * console.error('Token exchange failed:', error); + * } + * ``` + */ + private _buildTokenExchangeParams( + options: CustomTokenExchangeOptions + ): TokenExchangeRequestOptions { + return { + ...options, + grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', + subject_token: options.subject_token, + subject_token_type: options.subject_token_type, + ...(options.actor_token && { actor_token: options.actor_token }), + ...(options.actor_token_type && { actor_token_type: options.actor_token_type }), + scope: scopesToRequest( + this.scope, + options.scope, + options.audience || this.options.authorizationParams.audience + ), + audience: options.audience || this.options.authorizationParams.audience, + organization: options.organization || this.options.authorizationParams.organization + }; + } + + async loginWithCustomTokenExchange( + options: CustomTokenExchangeOptions + ): Promise { + return this._requestToken(this._buildTokenExchangeParams(options)); + } + + /** + * ```js + * await auth0.customTokenExchange(options); + * ``` + * + * Exchanges an external subject token for Auth0 tokens without affecting the current session. + * + * Unlike `loginWithCustomTokenExchange`, this method has no side effects — it does not cache + * tokens, does not update the authenticated session, and does not affect `isAuthenticated()` + * or `getUser()`. Use this for delegation or impersonation scenarios where you need a token + * for a downstream API but do not want to change who the current user is. + * + * When a Web Worker is configured, the refresh_token is discarded inside the worker and + * never reaches the main thread. When no Web Worker is configured, the raw authorization + * server response is returned — if a refresh_token is present, discard it; this method + * intentionally does not store it. + * + * @param {CustomTokenExchangeOptions} options - The options required to perform the token exchange. + * @returns {Promise} A promise that resolves to the token endpoint response. + * + * **Example:** + * ```js + * const tokenResponse = await auth0.customTokenExchange({ + * subject_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...', + * subject_token_type: 'urn:acme:legacy-system-token', + * actor_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...', + * actor_token_type: 'https://idp.example.com/token-type/agent', + * audience: 'https://api.example.com', + * }); + * + * // Use tokenResponse.access_token to call downstream API + * // Current user session is unchanged + * ``` + */ + async customTokenExchange( + options: CustomTokenExchangeOptions + ): Promise { + const params = this._buildTokenExchangeParams(options); + try { + const result = await oauthToken( + { + ...params, + baseUrl: this.domainUrl, + client_id: this.options.clientId, + auth0Client: this.options.auth0Client, + useFormData: this.options.useFormData, + timeout: this.httpTimeoutMs, + dpop: this.dpop, + }, + this.worker, + true // skipTokenStorage — when using a worker, refresh_token is discarded inside it + ); + + if (result.id_token) { + await this._verifyIdToken(result.id_token, undefined, options.organization); + } + + return result; + } catch (e) { + this._storeMfaContext(e, params.scope, params.audience); + throw e; + } + } + + /** + * @deprecated Use `loginWithCustomTokenExchange()` instead. This method will be removed in the next major version. + * + * Exchanges an external subject token for Auth0 tokens. + * + * @param {CustomTokenExchangeOptions} options - The options required to perform the token exchange. + * @returns {Promise} A promise that resolves to the token endpoint response. + * + * **Example:** + * ```js + * // Instead of: + * const tokens = await auth0.exchangeToken(options); + * + * // Use: + * const tokens = await auth0.loginWithCustomTokenExchange(options); + * ``` + */ + async exchangeToken( + options: CustomTokenExchangeOptions + ): Promise { + return this.loginWithCustomTokenExchange(options); + } + + protected _assertDpop(dpop: Dpop | undefined): asserts dpop is Dpop { + if (!dpop) { + throw new Error('`useDpop` option must be enabled before using DPoP.'); + } + } + + /** + * Returns the current DPoP nonce used for making requests to Auth0. + * + * It can return `undefined` because when starting fresh it will not + * be populated until after the first response from the server. + * + * It requires enabling the {@link Auth0ClientOptions.useDpop} option. + * + * @param nonce The nonce value. + * @param id The identifier of a nonce: if absent, it will get the nonce + * used for requests to Auth0. Otherwise, it will be used to + * select a specific non-Auth0 nonce. + */ + public getDpopNonce(id?: string): Promise { + this._assertDpop(this.dpop); + + return this.dpop.getNonce(id); + } + + /** + * Sets the current DPoP nonce used for making requests to Auth0. + * + * It requires enabling the {@link Auth0ClientOptions.useDpop} option. + * + * @param nonce The nonce value. + * @param id The identifier of a nonce: if absent, it will set the nonce + * used for requests to Auth0. Otherwise, it will be used to + * select a specific non-Auth0 nonce. + */ + public setDpopNonce(nonce: string, id?: string): Promise { + this._assertDpop(this.dpop); + + return this.dpop.setNonce(nonce, id); + } + + /** + * Returns a string to be used to demonstrate possession of the private + * key used to cryptographically bind access tokens with DPoP. + * + * It requires enabling the {@link Auth0ClientOptions.useDpop} option. + */ + public generateDpopProof(params: { + url: string; + method: string; + nonce?: string; + accessToken: string; + }): Promise { + this._assertDpop(this.dpop); + + return this.dpop.generateProof(params); + } + + /** + * Returns a new `Fetcher` class that will contain a `fetchWithAuth()` method. + * This is a drop-in replacement for the Fetch API's `fetch()` method, but will + * handle certain authentication logic for you, like building the proper auth + * headers or managing DPoP nonces and retries automatically. + * + * Check the `EXAMPLES.md` file for a deeper look into this method. + */ + public createFetcher( + config: FetcherConfig = {} + ): Fetcher { + return new Fetcher(config, { + isDpopEnabled: () => !!this.options.useDpop, + getAccessToken: authParams => + this.getTokenSilently({ + authorizationParams: { + scope: authParams?.scope?.join(' '), + audience: authParams?.audience + }, + detailedResponse: true + }), + getDpopNonce: () => this.getDpopNonce(config.dpopNonceId), + setDpopNonce: nonce => this.setDpopNonce(nonce, config.dpopNonceId), + generateDpopProof: params => this.generateDpopProof(params) + }); + } + + + /** + * Initiates a redirect to connect the user's account with a specified connection. + * This method generates PKCE parameters, creates a transaction, and redirects to the /connect endpoint. + * + * You must enable `Offline Access` from the Connection Permissions settings to be able to use the connection with Connected Accounts. + * + * @template TAppState - The application state to persist through the transaction. + * @param {RedirectConnectAccountOptions} options - Options for the connect account redirect flow. + * @param {string} options.connection - The name of the connection to link (e.g. 'google-oauth2'). + * @param {string[]} [options.scopes] - Array of scopes to request from the Identity Provider during the connect account flow. + * @param {AuthorizationParams} [options.authorization_params] - Additional authorization parameters for the request to the upstream IdP. + * @param {string} [options.redirectUri] - The URI to redirect back to after connecting the account. + * @param {TAppState} [options.appState] - Application state to persist through the transaction. + * @param {(url: string) => Promise} [options.openUrl] - Custom function to open the URL. + * + * @returns {Promise} Resolves when the redirect is initiated. + * @throws {MyAccountApiError} If the connect request to the My Account API fails. + */ + public async connectAccountWithRedirect( + options: RedirectConnectAccountOptions + ) { + const { + openUrl, + appState, + connection, + scopes, + authorization_params, + redirectUri = this.options.authorizationParams.redirect_uri || + window.location.origin + } = options; + + if (!connection) { + throw new Error('connection is required'); + } + + const state = encode(createRandomString()); + const code_verifier = createRandomString(); + const code_challengeBuffer = await sha256(code_verifier); + const code_challenge = bufferToBase64UrlEncoded(code_challengeBuffer); + + const { connect_uri, connect_params, auth_session } = + await this.myAccount.connectAccount({ + connection, + scopes, + redirect_uri: redirectUri, + state, + code_challenge, + code_challenge_method: 'S256', + authorization_params + }); + + this.transactionManager.create({ + state, + code_verifier, + auth_session, + redirect_uri: redirectUri, + appState, + connection, + response_type: ResponseType.ConnectCode + }); + + const url = new URL(connect_uri); + url.searchParams.set('ticket', connect_params.ticket); + if (openUrl) { + await openUrl(url.toString()); + } else { + window.location.assign(url); + } + } + + /** + * @internal + * Internal method used by PasskeyApiClient to exchange passkey credentials for tokens. + * Routes through _requestToken() so tokens are cached, ID token verified, and session established. + */ + async _requestTokenForPasskey( + options: { + authSession: string; + credential: PasskeyCredentialResponse; + realm?: string; + scope?: string; + audience?: string; + organization?: string; + } + ): Promise { + const audience = options.audience || this.options.authorizationParams.audience; + const organization = options.organization || this.options.authorizationParams.organization; + return this._requestToken({ + grant_type: 'urn:okta:params:oauth:grant-type:webauthn', + auth_session: options.authSession, + authn_response: options.credential, + ...(options.realm && { realm: options.realm }), + ...(organization && { organization }), + scope: scopesToRequest(this.scope, options.scope, audience), + audience, + }); + } + + /** + * @internal + * Internal method used by MfaApiClient to exchange MFA tokens for access tokens. + * This method should not be called directly by applications. + */ + async _requestTokenForMfa( + options: { + grant_type: string; + mfaToken: string; + scope?: string; + audience?: string; + otp?: string; + binding_code?: string; + oob_code?: string; + recovery_code?: string; + }, + additionalParameters?: RequestTokenAdditionalParameters + ): Promise { + // Need to add better typing here + const { mfaToken, ...restOptions } = options; + return this._requestToken({ ...restOptions, mfa_token: mfaToken } as any, additionalParameters); + } +} + +interface BaseRequestTokenOptions { + audience?: string; + scope: string; + timeout?: number; + redirect_uri?: string; +} + +interface PKCERequestTokenOptions extends BaseRequestTokenOptions { + code: string; + grant_type: 'authorization_code'; + code_verifier: string; +} + +interface RefreshTokenRequestTokenOptions extends BaseRequestTokenOptions { + grant_type: 'refresh_token'; + refresh_token?: string; +} + +interface TokenExchangeRequestOptions extends BaseRequestTokenOptions { + grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange'; + subject_token: string; + subject_token_type: string; + actor_token?: string; + actor_token_type?: string; + organization?: string; +} + +interface WebauthnRequestTokenOptions extends BaseRequestTokenOptions { + grant_type: 'urn:okta:params:oauth:grant-type:webauthn'; + auth_session: string; + authn_response: PasskeyCredentialResponse; + realm?: string; + organization?: string; +} + +interface RequestTokenAdditionalParameters { + nonceIn?: string; + organization?: string; + scopesToRequest?: string; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.utils.ts b/backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.utils.ts new file mode 100644 index 00000000..b87c816f --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/Auth0Client.utils.ts @@ -0,0 +1,226 @@ +import { ICache, InMemoryCache, LocalStorageCache } from './cache'; +import { + Auth0ClientOptions, + AuthorizationParams, + AuthorizeOptions, + ClientAuthorizationParams, + LogoutOptions +} from './global'; +import { scopesToRequest } from './scope'; +import { ONLINE_ACCESS_SCOPE } from './constants'; + +/** + * @ignore + */ +export const GET_TOKEN_SILENTLY_LOCK_KEY = 'auth0.lock.getTokenSilently'; + +/** + * @ignore + */ +export const GET_TOKEN_FROM_IFRAME_LOCK_KEY = 'auth0.lock.getTokenFromIFrame'; + +/** + * @ignore + */ +export const buildGetTokenSilentlyLockKey = ( + clientId: string, + audience: string +) => `${GET_TOKEN_SILENTLY_LOCK_KEY}.${clientId}.${audience}`; + +/** + * @ignore + * Builds a global lock key for iframe-based authentication flows. + * This ensures only one iframe authorization request runs at a time per client, + * preventing "Invalid state" errors from concurrent iframe requests overwriting + * each other's state in the Auth0 session. + */ +export const buildIframeLockKey = (clientId: string) => + `${GET_TOKEN_FROM_IFRAME_LOCK_KEY}.${clientId}`; + +/** + * @ignore + */ +export const buildOrganizationHintCookieName = (clientId: string) => + `auth0.${clientId}.organization_hint`; + +/** + * @ignore + */ +export const OLD_IS_AUTHENTICATED_COOKIE_NAME = 'auth0.is.authenticated'; + +/** + * @ignore + */ +export const buildIsAuthenticatedCookieName = (clientId: string) => + `auth0.${clientId}.is.authenticated`; + +/** + * @ignore + */ +const cacheLocationBuilders: Record ICache> = { + memory: () => new InMemoryCache().enclosedCache, + localstorage: () => new LocalStorageCache() +}; + +/** + * @ignore + */ +export const cacheFactory = (location: string) => { + return cacheLocationBuilders[location]; +}; + +/** + * @ignore + */ +export const getAuthorizeParams = ( + clientOptions: Auth0ClientOptions & { + authorizationParams: ClientAuthorizationParams; + }, + scope: Record, + authorizationParams: AuthorizationParams & { scope?: string }, + state: string, + nonce: string, + code_challenge: string, + redirect_uri: string | undefined, + response_mode: string | undefined, + thumbprint: string | undefined +): AuthorizeOptions => { + return { + client_id: clientOptions.clientId, + ...clientOptions.authorizationParams, + ...authorizationParams, + scope: scopesToRequest(scope, authorizationParams.scope, authorizationParams.audience), + response_type: 'code', + response_mode: response_mode || 'query', + state, + nonce, + redirect_uri: + redirect_uri || clientOptions.authorizationParams.redirect_uri, + code_challenge, + code_challenge_method: 'S256', + dpop_jkt: thumbprint + }; +}; + +/** + * @ignore + * + * Function used to provide support for the deprecated onRedirect through openUrl. + */ +export const patchOpenUrlWithOnRedirect = < + T extends Pick +>( + options: T +) => { + const { openUrl, onRedirect, ...originalOptions } = options; + + const result = { + ...originalOptions, + openUrl: openUrl === false || openUrl ? openUrl : onRedirect + }; + + return result as T; +}; + +/** + * @ignore + * + * Function used to trim the `online_access` scope from a list of scopes. + * This is used in the MRRT flow to avoid spurious missing scope errors, + * since `online_access` is never echoed back in the response. + */ +const withoutOnlineAccessScope = (scopes: string[]): string[] => + scopes.filter((scope) => scope !== ONLINE_ACCESS_SCOPE); + +/** + * @ignore + * + * Checks if all scopes are included inside other array of scopes + */ +export const allScopesAreIncluded = (scopeToInclude?: string, scopes?: string): boolean => { + const scopeGroup = scopes?.split(" ") || []; + const scopesToInclude = scopeToInclude?.split(" ") || []; + return scopesToInclude.every((key) => scopeGroup.includes(key)); +} + +/** + * @ignore + * + * Returns the scopes that are missing after a refresh. + * + * When `onlineAccess` is true, `online_access` is stripped from the requested scopes before + * comparing: in online mode the server reuses the same (non-rotating) ORT for an MRRT + * cross-audience exchange and never echoes `online_access` back in the response scope, so + * comparing it would flag a spurious missing scope. Outside online mode the strip is not + * applied, so a genuinely missing `online_access` is still reported. + */ +export const getMissingScopes = ( + requestedScope?: string, + respondedScope?: string, + onlineAccess?: boolean +): string => { + const splitRequested = requestedScope?.split(" ") || []; + const requestedScopes = onlineAccess + ? withoutOnlineAccessScope(splitRequested) + : splitRequested; + const respondedScopes = respondedScope?.split(" ") || []; + + const missingScopes = requestedScopes.filter((scope) => respondedScopes.indexOf(scope) == -1); + + return missingScopes.join(","); +} + +/** + * @ignore + * + * For backward compatibility we are going to check if we are going to downscope while doing a refresh request + * while MRRT is allowed. If the audience is the same for the refresh_token we are going to use and it has + * lower scopes than the ones originally in the token, we are going to return the scopes that were stored + * with the refresh_token in the tokenset. + * @param useMrrt Setting that the user can activate to use MRRT in their requests + * @param authorizationParams Contains the audience and scope that the user requested to obtain a token + * @param cachedAudience Audience stored with the refresh_token wich we are going to use in the request + * @param cachedScope Scope stored with the refresh_token wich we are going to use in the request + */ +export const getScopeToRequest = ( + useMrrt: boolean | undefined, + authorizationParams: { audience?: string, scope: string }, + cachedAudience?: string, + cachedScope?: string +): string => { + if (useMrrt && cachedAudience && cachedScope) { + if (authorizationParams.audience !== cachedAudience) { + return authorizationParams.scope; + } + + const cachedScopes = cachedScope.split(" "); + const newScopes = authorizationParams.scope?.split(" ") || []; + const newScopesAreIncluded = newScopes.every((scope) => cachedScopes.includes(scope)); + + return cachedScopes.length >= newScopes.length && newScopesAreIncluded ? cachedScope : authorizationParams.scope; + } + + return authorizationParams.scope; +} + +/** + * @ignore + * + * Checks if the refresh request has been done using MRRT + * @param cachedAudience Audience from the refresh token used to refresh + * @param cachedScope Scopes from the refresh token used to refresh + * @param requestAudience Audience sent to the server + * @param requestScope Scopes sent to the server + */ +export const isRefreshWithMrrt = ( + cachedAudience: string | undefined, + cachedScope: string | undefined, + requestAudience: string | undefined, + requestScope: string, +): boolean => { + if (cachedAudience !== requestAudience) { + return true; + } + + return !allScopesAreIncluded(requestScope, cachedScope); +} \ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/TokenExchange.ts b/backend/node_modules/@auth0/auth0-spa-js/src/TokenExchange.ts new file mode 100644 index 00000000..1860c9be --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/TokenExchange.ts @@ -0,0 +1,106 @@ +/** + * Represents the configuration options required for initiating a Custom Token Exchange request + * following RFC 8693 specifications. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc8693 | RFC 8693: OAuth 2.0 Token Exchange} + */ +export type CustomTokenExchangeOptions = { + /** + * The type identifier for the subject token being exchanged + * + * @pattern + * - Must be a namespaced URI under your organization's control + * - Forbidden patterns: + * - `^urn:ietf:params:oauth:*` (IETF reserved) + * - `^https:\/\/auth0\.com/*` (Auth0 reserved) + * - `^urn:auth0:*` (Auth0 reserved) + * + * @example + * "urn:acme:legacy-system-token" + * "https://api.yourcompany.com/token-type/v1" + */ + subject_token_type: string; + + /** + * The opaque token value being exchanged for Auth0 tokens + * + * @security + * - Must be validated in Auth0 Actions using strong cryptographic verification + * - Implement replay attack protection + * - Recommended validation libraries: `jose`, `jsonwebtoken` + * + * @example + * "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + */ + subject_token: string; + + /** + * The target audience for the requested Auth0 token + * + * @remarks + * Must match exactly with an API identifier configured in your Auth0 tenant. + * If not provided, falls back to the client's default audience. + * + * @example + * "https://api.your-service.com/v1" + */ + audience?: string; + + /** + * Space-separated list of OAuth 2.0 scopes being requested + * + * @remarks + * Subject to API authorization policies configured in Auth0 + * + * @example + * "openid profile email read:data write:data" + */ + scope?: string; + + /** + * ID or name of the organization to use when authenticating a user. + * When provided, the user will be authenticated using the organization context. + * The organization ID will be present in the access token payload. + */ + organization?: string; + + /** + * A token representing the acting party for delegation or impersonation scenarios. + * + * @remarks + * Used when one principal needs to act on behalf of another. + * For example, an AI agent acting on behalf of a user. + * + * @example + * "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + */ + actor_token?: string; + + /** + * The type identifier for the actor token. + * + * @remarks + * Must be a URI under your organization's control, following the same + * rules as subject_token_type. + * + * @example + * "https://idp.example.com/token-type/agent" + */ + actor_token_type?: string; + + /** + * Additional custom parameters for Auth0 Action processing + * + * @remarks + * Accessible in Action code via `event.request.body` + * + * @example + * ```typescript + * { + * custom_parameter: "session_context", + * device_fingerprint: "a3d8f7...", + * } + * ``` + */ + [key: string]: unknown; +}; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/api.ts b/backend/node_modules/@auth0/auth0-spa-js/src/api.ts new file mode 100644 index 00000000..f4d7cc2b --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/api.ts @@ -0,0 +1,187 @@ +import { TokenEndpointOptions, TokenEndpointResponse } from './global'; +import { + DEFAULT_AUTH0_CLIENT, + DEFAULT_AUDIENCE, + DEFAULT_FETCH_TIMEOUT_MS +} from './constants'; + +/** + * @ignore + * Internal options for the revokeToken API call. + * Kept in api.ts (not global.ts) so it is not part of the public type surface. + */ +interface RevokeTokenOptions { + baseUrl: string; + /** Maps directly to the OAuth `client_id` parameter. */ + client_id: string; + /** Tokens to revoke. Empty for the worker path — the worker holds its own store. */ + refreshTokens: string[]; + audience?: string; + timeout?: number; + auth0Client?: any; + useFormData?: boolean; + onRefreshTokenRevoked?: (refreshToken: string) => Promise | void; +} +import * as dpopUtils from './dpop/utils'; +import { GenericError } from './errors'; +import { getJSON, fetchWithTimeout } from './http'; +import { sendMessage } from './worker/worker.utils'; +import { createQueryParams, stripAuth0Client } from './utils'; + +export async function oauthToken( + { + baseUrl, + timeout, + audience, + scope, + auth0Client, + useFormData, + useMrrt, + dpop, + preserveRefreshToken, + ...options + }: TokenEndpointOptions, + worker?: Worker, + skipTokenStorage?: boolean +) { + const isTokenExchange = + options.grant_type === 'urn:ietf:params:oauth:grant-type:token-exchange'; + + const isWebAuthn = + options.grant_type === 'urn:okta:params:oauth:grant-type:webauthn'; + + const refreshWithMrrt = options.grant_type === 'refresh_token' && useMrrt; + + const includeAudienceAndScope = isTokenExchange || isWebAuthn || refreshWithMrrt; + + const allParams = { + ...options, + ...(includeAudienceAndScope && audience && { audience }), + ...(includeAudienceAndScope && scope && { scope }), + }; + + // WebAuthn grant must use JSON so authn_response is sent as an object, not a string. + const useJson = isWebAuthn || !useFormData; + + const body = useJson + ? JSON.stringify(allParams) + : createQueryParams(allParams); + + const isDpopSupported = dpopUtils.isGrantTypeSupported(options.grant_type); + + return await getJSON( + `${baseUrl}/oauth/token`, + timeout, + audience || DEFAULT_AUDIENCE, + scope, + { + method: 'POST', + body, + headers: { + 'Content-Type': useJson + ? 'application/json' + : 'application/x-www-form-urlencoded', + 'Auth0-Client': btoa( + JSON.stringify(stripAuth0Client(auth0Client || DEFAULT_AUTH0_CLIENT)) + ) + } + }, + worker, + useFormData, + useMrrt, + isDpopSupported ? dpop : undefined, + undefined, + skipTokenStorage, + preserveRefreshToken + ); +} + +/** + * Revokes refresh tokens using the /oauth/revoke endpoint. + * + * Mirrors the oauthToken pattern: the worker/non-worker dispatch lives here, + * keeping Auth0Client free of transport concerns. + * + * - Worker path: sends a single message; the worker holds its own RT store and + * loops internally. refreshTokens is empty (worker ignores it). + * - Non-worker path: loops over refreshTokens and issues one request per token. + * + * @throws {GenericError} If any revoke request fails + */ +export async function revokeToken( + { + baseUrl, + timeout, + auth0Client, + useFormData, + refreshTokens, + audience, + client_id, + onRefreshTokenRevoked + }: RevokeTokenOptions, + worker?: Worker +): Promise { + const resolvedTimeout = timeout || DEFAULT_FETCH_TIMEOUT_MS; + // token_type_hint is a SHOULD per RFC 7009 §2.1; used in both paths below. + const token_type_hint = 'refresh_token' as const; + const fetchUrl = `${baseUrl}/oauth/revoke`; + const headers = { + 'Content-Type': useFormData + ? 'application/x-www-form-urlencoded' + : 'application/json', + 'Auth0-Client': btoa( + JSON.stringify(stripAuth0Client(auth0Client || DEFAULT_AUTH0_CLIENT)) + ) + }; + + if (worker) { + // Worker holds its own RT store and injects each token into the request. + // Send the base body (without token) so the worker can loop over its tokens. + const baseParams = { client_id, token_type_hint }; + const body = useFormData + ? createQueryParams(baseParams) + : JSON.stringify(baseParams); + + try { + return await sendMessage( + { + type: 'revoke', + timeout: resolvedTimeout, + fetchUrl, + fetchOptions: { method: 'POST', body, headers }, + useFormData, + auth: { audience: audience ?? DEFAULT_AUDIENCE } + }, + worker + ); + } catch (e) { + throw new GenericError('revoke_error', (e as Error).message); + } + } + + for (const refreshToken of refreshTokens) { + const params = { client_id, token_type_hint, token: refreshToken }; + const body = useFormData + ? createQueryParams(params) + : JSON.stringify(params); + + const response = await fetchWithTimeout( + fetchUrl, + { method: 'POST', body, headers }, + resolvedTimeout + ); + + if (!response.ok) { + let error: string | undefined; + let errorDescription: string | undefined; + try { + ({ error, error_description: errorDescription } = JSON.parse(await response.text())); + } catch { + // body absent or not valid JSON + } + throw new GenericError(error || 'revoke_error', errorDescription || `HTTP error ${response.status}`); + } + + await onRefreshTokenRevoked?.(refreshToken); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-localstorage.ts b/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-localstorage.ts new file mode 100644 index 00000000..2253de63 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-localstorage.ts @@ -0,0 +1,31 @@ +import { ICache, Cacheable, CACHE_KEY_PREFIX, MaybePromise } from './shared'; + +export class LocalStorageCache implements ICache { + public set(key: string, entry: T) { + localStorage.setItem(key, JSON.stringify(entry)); + } + + public get(key: string): MaybePromise { + const json = window.localStorage.getItem(key); + + if (!json) return; + + try { + const payload = JSON.parse(json) as T; + return payload; + /* c8 ignore next 3 */ + } catch (e) { + return; + } + } + + public remove(key: string) { + localStorage.removeItem(key); + } + + public allKeys() { + return Object.keys(window.localStorage).filter(key => + key.startsWith(CACHE_KEY_PREFIX) + ); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-manager.ts b/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-manager.ts new file mode 100644 index 00000000..8a0f25ba --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-manager.ts @@ -0,0 +1,389 @@ +import { DEFAULT_NOW_PROVIDER } from '../constants'; +import { CacheKeyManifest } from './key-manifest'; + +import { + CacheEntry, + ICache, + CacheKey, + CACHE_KEY_PREFIX, + WrappedCacheEntry, + DecodedToken, + CACHE_KEY_ID_TOKEN_SUFFIX, + IdTokenEntry +} from './shared'; + +const DEFAULT_EXPIRY_ADJUSTMENT_SECONDS = 0; + +export class CacheManager { + private nowProvider: () => number | Promise; + + constructor( + private cache: ICache, + private keyManifest?: CacheKeyManifest, + nowProvider?: () => number | Promise + ) { + this.nowProvider = nowProvider || DEFAULT_NOW_PROVIDER; + } + + async setIdToken( + clientId: string, + idToken: string, + decodedToken: DecodedToken + ): Promise { + const cacheKey = this.getIdTokenCacheKey(clientId); + await this.cache.set(cacheKey, { + id_token: idToken, + decodedToken + }); + await this.keyManifest?.add(cacheKey); + } + + async getIdToken(cacheKey: CacheKey): Promise { + const entry = await this.cache.get( + this.getIdTokenCacheKey(cacheKey.clientId) + ); + + if (!entry && cacheKey.scope && cacheKey.audience) { + const entryByScope = await this.get(cacheKey); + + if (!entryByScope) { + return; + } + + if (!entryByScope.id_token || !entryByScope.decodedToken) { + return; + } + + return { + id_token: entryByScope.id_token, + decodedToken: entryByScope.decodedToken + }; + } + + if (!entry) { + return; + } + + return { id_token: entry.id_token, decodedToken: entry.decodedToken }; + } + + async get( + cacheKey: CacheKey, + expiryAdjustmentSeconds = DEFAULT_EXPIRY_ADJUSTMENT_SECONDS, + useMrrt = false, + cacheMode?: string + ): Promise | undefined> { + let wrappedEntry = await this.cache.get( + cacheKey.toKey() + ); + + // Track the key where the entry was actually found, so that + // expiry-related writes (strip / remove) target the correct entry + // instead of creating a ghost entry under the lookup key. + let resolvedCacheKey = cacheKey; + + if (!wrappedEntry) { + const keys = await this.getCacheKeys(); + + if (!keys) return; + + const matchedKey = this.matchExistingCacheKey(cacheKey, keys); + + if (matchedKey) { + wrappedEntry = await this.cache.get(matchedKey); + resolvedCacheKey = CacheKey.fromKey(matchedKey); + } + + // To refresh using MRRT we need to send a request to the server + // If cacheMode is 'cache-only', this will make us unable to call the server + // so it won't be needed to find a valid refresh token + if (!wrappedEntry && useMrrt && cacheMode !== 'cache-only') { + return this.getEntryWithRefreshToken(cacheKey, keys); + } + } + + // If we still don't have an entry, exit. + if (!wrappedEntry) { + return; + } + + const now = await this.nowProvider(); + const nowSeconds = Math.floor(now / 1000); + + if (wrappedEntry.expiresAt - expiryAdjustmentSeconds < nowSeconds) { + if (wrappedEntry.body.refresh_token) { + return this.modifiedCachedEntry(wrappedEntry, resolvedCacheKey); + } + + await this.cache.remove(resolvedCacheKey.toKey()); + await this.keyManifest?.remove(resolvedCacheKey.toKey()); + + return; + } + + return wrappedEntry.body; + } + + private async modifiedCachedEntry(wrappedEntry: WrappedCacheEntry, cacheKey: CacheKey): Promise> { + // We need to keep audience and scope in order to check them later when doing refresh + // using MRRT. See getScopeToRequest method. + // + // Build a new object instead of mutating wrappedEntry.body in-place, + // because InMemoryCache returns direct references — mutating would + // corrupt the original entry stored under a different (superset) key. + const strippedBody: Partial = { + refresh_token: wrappedEntry.body.refresh_token, + audience: wrappedEntry.body.audience, + scope: wrappedEntry.body.scope, + }; + + const strippedEntry: WrappedCacheEntry = { + body: strippedBody, + expiresAt: wrappedEntry.expiresAt, + }; + + await this.cache.set(cacheKey.toKey(), strippedEntry); + + return { + refresh_token: strippedBody.refresh_token, + audience: strippedBody.audience, + scope: strippedBody.scope, + }; + } + + async set(entry: CacheEntry): Promise { + const cacheKey = new CacheKey({ + clientId: entry.client_id, + scope: entry.scope, + audience: entry.audience + }); + + const wrappedEntry = await this.wrapCacheEntry(entry); + + await this.cache.set(cacheKey.toKey(), wrappedEntry); + await this.keyManifest?.add(cacheKey.toKey()); + } + + async remove( + client_id: string, + audience?: string, + scope?: string, + ): Promise { + const cacheKey = new CacheKey({ + clientId: client_id, + scope: scope, + audience: audience + }); + + await this.cache.remove(cacheKey.toKey()); + } + + async stripRefreshToken(refreshToken: string): Promise { + const keys = await this.getCacheKeys(); + + /* c8 ignore next */ + if (!keys) return; + + // Find all cache entries that have this refresh token and strip only the refresh token, + // leaving the access token intact (it remains valid until it expires) + for (const key of keys) { + const entry = await this.cache.get(key); + if (entry?.body?.refresh_token === refreshToken) { + delete entry.body.refresh_token; + await this.cache.set(key, entry); + } + } + } + + async clear(clientId?: string): Promise { + const keys = await this.getCacheKeys(); + + /* c8 ignore next */ + if (!keys) return; + + await keys + .filter(key => (clientId ? key.includes(clientId) : true)) + .reduce(async (memo, key) => { + await memo; + await this.cache.remove(key); + }, Promise.resolve()); + + await this.keyManifest?.clear(); + } + + private async wrapCacheEntry(entry: CacheEntry): Promise { + const now = await this.nowProvider(); + const expiresInTime = Math.floor(now / 1000) + entry.expires_in; + + return { + body: entry, + expiresAt: expiresInTime + }; + } + + private async getCacheKeys(): Promise { + if (this.keyManifest) { + return (await this.keyManifest.get())?.keys; + } else if (this.cache.allKeys) { + return this.cache.allKeys(); + } + } + + /** + * Returns the cache key to be used to store the id token + * @param clientId The client id used to link to the id token + * @returns The constructed cache key, as a string, to store the id token + */ + private getIdTokenCacheKey(clientId: string) { + return new CacheKey( + { clientId }, + CACHE_KEY_PREFIX, + CACHE_KEY_ID_TOKEN_SUFFIX + ).toKey(); + } + + /** + * Finds the corresponding key in the cache based on the provided cache key. + * The keys inside the cache are in the format {prefix}::{clientId}::{audience}::{scope}. + * The first key in the cache that satisfies the following conditions is returned + * - `prefix` is strict equal to Auth0's internally configured `keyPrefix` + * - `clientId` is strict equal to the `cacheKey.clientId` + * - `audience` is strict equal to the `cacheKey.audience` + * - `scope` contains at least all the `cacheKey.scope` values + * * + * @param keyToMatch The provided cache key + * @param allKeys A list of existing cache keys + */ + private matchExistingCacheKey(keyToMatch: CacheKey, allKeys: Array) { + return allKeys.filter(key => { + const cacheKey = CacheKey.fromKey(key); + const scopeSet = new Set(cacheKey.scope && cacheKey.scope.split(' ')); + const scopesToMatch = keyToMatch.scope?.split(' ') || []; + + const hasAllScopes = + cacheKey.scope && + scopesToMatch.reduce( + (acc, current) => acc && scopeSet.has(current), + true + ); + + return ( + cacheKey.prefix === CACHE_KEY_PREFIX && + cacheKey.clientId === keyToMatch.clientId && + cacheKey.audience === keyToMatch.audience && + hasAllScopes + ); + })[0]; + } + + /** + * Returns the first entry that contains a refresh_token that satisfies the following conditions + * The keys inside the cache are in the format {prefix}::{clientId}::{audience}::{scope}. + * - `prefix` is strict equal to Auth0's internally configured `keyPrefix` + * - `clientId` is strict equal to the `cacheKey.clientId` + * @param keyToMatch The provided cache key + * @param allKeys A list of existing cache keys + */ + private async getEntryWithRefreshToken(keyToMatch: CacheKey, allKeys: Array): Promise | undefined> { + for (const key of allKeys) { + const cacheKey = CacheKey.fromKey(key); + + if (cacheKey.prefix === CACHE_KEY_PREFIX && + cacheKey.clientId === keyToMatch.clientId) { + const cachedEntry = await this.cache.get(key); + + if (cachedEntry?.body?.refresh_token) { + return { + refresh_token: cachedEntry.body.refresh_token, + audience: cachedEntry.body.audience, + scope: cachedEntry.body.scope, + }; + } + } + } + + return undefined; + } + + /** + * Returns all distinct refresh tokens stored for a given audience and client. + * + * Multiple cache entries may exist for the same audience when different scope + * combinations were obtained via separate authorization flows, each potentially + * carrying a different refresh token. A Set is used to deduplicate tokens that + * are shared across entries (e.g. MRRT). + * + * @param audience The audience to look up + * @param clientId The client id to scope the lookup + */ + async getRefreshTokensByAudience( + audience: string, + clientId: string + ): Promise { + const keys = await this.getCacheKeys(); + + if (!keys) return []; + + const tokens = new Set(); + + for (const key of keys) { + const cacheKey = CacheKey.fromKey(key); + + if ( + cacheKey.prefix === CACHE_KEY_PREFIX && + cacheKey.clientId === clientId && + cacheKey.audience === audience + ) { + const entry = await this.cache.get(key); + + if (entry?.body?.refresh_token) { + tokens.add(entry.body.refresh_token); + } + } + } + + return Array.from(tokens); + } + + /** + * Updates the refresh token in cache entries after a rotation. + * + * When a refresh token is rotated, multiple cache entries (for different audiences/scopes) + * may share the same refresh token. This method propagates the new refresh token to all + * matching entries. + * + * With MRRT enabled, concurrent `getTokenSilently()` calls for different audiences all + * read the same RT and exchange it in parallel. Each exchange returns a different rotated + * RT, which would leave the client with a diverged set of forked tokens per audience. + * Passing `useMrrt = true` unconditionally overwrites every entry that has *any* + * refresh token, so the last concurrent call to complete always wins and the client + * converges to a single shared RT. + * + * @param oldRefreshToken The refresh token that was used and is now invalid + * @param newRefreshToken The new refresh token received from the server + * @param clientId The client ID whose entries should be updated + * @param useMrrt When true, update all entries regardless of their current RT value + */ + async updateEntry( + oldRefreshToken: string, + newRefreshToken: string, + clientId: string, + useMrrt = false + ): Promise { + const allKeys = await this.getCacheKeys(); + if (!allKeys) return; + + for (const key of allKeys) { + if (CacheKey.fromKey(key).clientId !== clientId) continue; + + const entry = await this.cache.get(key); + if (!entry?.body) continue; + + const rt = entry.body.refresh_token; + if (rt && (useMrrt || rt === oldRefreshToken)) { + entry.body.refresh_token = newRefreshToken; + await this.cache.set(key, entry); + } + } + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-memory.ts b/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-memory.ts new file mode 100644 index 00000000..7dde0710 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/cache/cache-memory.ts @@ -0,0 +1,31 @@ +import { Cacheable, ICache, MaybePromise } from './shared'; + +export class InMemoryCache { + public enclosedCache: ICache = (function () { + let cache: Record = {}; + + return { + set(key: string, entry: T) { + cache[key] = entry; + }, + + get(key: string): MaybePromise { + const cacheEntry = cache[key] as T; + + if (!cacheEntry) { + return; + } + + return cacheEntry; + }, + + remove(key: string) { + delete cache[key]; + }, + + allKeys(): string[] { + return Object.keys(cache); + } + }; + })(); +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/cache/index.ts b/backend/node_modules/@auth0/auth0-spa-js/src/cache/index.ts new file mode 100644 index 00000000..523c8540 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/cache/index.ts @@ -0,0 +1,4 @@ +export * from './cache-localstorage'; +export * from './cache-memory'; +export * from './cache-manager'; +export * from './shared'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/cache/key-manifest.ts b/backend/node_modules/@auth0/auth0-spa-js/src/cache/key-manifest.ts new file mode 100644 index 00000000..2a7d33e4 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/cache/key-manifest.ts @@ -0,0 +1,53 @@ +import { + CACHE_KEY_PREFIX, + ICache, + KeyManifestEntry, + MaybePromise +} from './shared'; + +export class CacheKeyManifest { + private readonly manifestKey: string; + + constructor(private cache: ICache, private clientId: string) { + this.manifestKey = this.createManifestKeyFrom(this.clientId); + } + + async add(key: string): Promise { + const keys = new Set( + (await this.cache.get(this.manifestKey))?.keys || [] + ); + + keys.add(key); + + await this.cache.set(this.manifestKey, { + keys: [...keys] + }); + } + + async remove(key: string): Promise { + const entry = await this.cache.get(this.manifestKey); + + if (entry) { + const keys = new Set(entry.keys); + keys.delete(key); + + if (keys.size > 0) { + return await this.cache.set(this.manifestKey, { keys: [...keys] }); + } + + return await this.cache.remove(this.manifestKey); + } + } + + get(): MaybePromise { + return this.cache.get(this.manifestKey); + } + + clear(): MaybePromise { + return this.cache.remove(this.manifestKey); + } + + private createManifestKeyFrom(clientId: string): string { + return `${CACHE_KEY_PREFIX}::${clientId}`; + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/cache/shared.ts b/backend/node_modules/@auth0/auth0-spa-js/src/cache/shared.ts new file mode 100644 index 00000000..f3d54998 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/cache/shared.ts @@ -0,0 +1,105 @@ +import { IdToken, User } from '../global'; + +export const CACHE_KEY_PREFIX = '@@auth0spajs@@'; +export const CACHE_KEY_ID_TOKEN_SUFFIX = '@@user@@'; + +export type CacheKeyData = { + audience?: string; + scope?: string; + clientId: string; +}; + +export class CacheKey { + public clientId: string; + public scope?: string; + public audience?: string; + + constructor( + data: CacheKeyData, + public prefix: string = CACHE_KEY_PREFIX, + public suffix?: string + ) { + this.clientId = data.clientId; + this.scope = data.scope; + this.audience = data.audience; + } + + /** + * Converts this `CacheKey` instance into a string for use in a cache + * @returns A string representation of the key + */ + toKey(): string { + return [this.prefix, this.clientId, this.audience, this.scope, this.suffix] + .filter(Boolean) + .join('::'); + } + + /** + * Converts a cache key string into a `CacheKey` instance. + * @param key The key to convert + * @returns An instance of `CacheKey` + */ + static fromKey(key: string): CacheKey { + const [prefix, clientId, audience, scope] = key.split('::'); + + return new CacheKey({ clientId, scope, audience }, prefix); + } + + /** + * Utility function to build a `CacheKey` instance from a cache entry + * @param entry The entry + * @returns An instance of `CacheKey` + */ + static fromCacheEntry(entry: CacheEntry): CacheKey { + const { scope, audience, client_id: clientId } = entry; + + return new CacheKey({ + scope, + audience, + clientId + }); + } +} + +export interface DecodedToken { + claims: IdToken; + user: User; +} + +export interface IdTokenEntry { + id_token: string; + decodedToken: DecodedToken; +} + +export type CacheEntry = { + id_token?: string; + token_type?: string; + access_token: string; + expires_in: number; + decodedToken?: DecodedToken; + audience: string; + scope: string; + client_id: string; + refresh_token?: string; + oauthTokenScope?: string; +}; + +export type WrappedCacheEntry = { + body: Partial; + expiresAt: number; +}; + +export type KeyManifestEntry = { + keys: string[]; +}; + +export type Cacheable = WrappedCacheEntry | KeyManifestEntry; + +export type MaybePromise = Promise | T; + +export interface ICache { + set(key: string, entry: T): MaybePromise; + get(key: string): MaybePromise; + remove(key: string): MaybePromise; + allKeys?(): MaybePromise; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/constants.ts b/backend/node_modules/@auth0/auth0-spa-js/src/constants.ts new file mode 100644 index 00000000..de2e4b98 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/constants.ts @@ -0,0 +1,85 @@ +import { PopupConfigOptions } from './global'; +import version from './version'; + +/** + * @ignore + */ +export const DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS = 60; + +/** + * @ignore + */ +export const DEFAULT_POPUP_CONFIG_OPTIONS: PopupConfigOptions = { + timeoutInSeconds: DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS +}; + +/** + * @ignore + */ +export const DEFAULT_SILENT_TOKEN_RETRY_COUNT = 3; + +/** + * @ignore + */ +export const CLEANUP_IFRAME_TIMEOUT_IN_SECONDS = 2; + +/** + * @ignore + */ +export const DEFAULT_FETCH_TIMEOUT_MS = 10000; + +export const CACHE_LOCATION_MEMORY = 'memory'; +export const CACHE_LOCATION_LOCAL_STORAGE = 'localstorage'; + +/** + * @ignore + */ +export const MISSING_REFRESH_TOKEN_ERROR_MESSAGE = 'Missing Refresh Token'; + +/** + * @ignore + */ +export const INVALID_REFRESH_TOKEN_ERROR_MESSAGE = 'invalid refresh token'; + +/** + * @ignore + */ +export const USER_BLOCKED_ERROR_MESSAGE = 'user is blocked'; + +/** + * @ignore + * The error_description returned by the /authorize endpoint when MFA is required + * but prompt=none prevents interaction (iframe silent auth flow). + */ +export const MFA_STEP_UP_ERROR_DESCRIPTION = 'Multifactor authentication required'; + +/** + * @ignore + */ +export const DEFAULT_SCOPE = 'openid profile email'; + +/** + * @ignore + * The scope that requests an Online Refresh Token (non-rotating, session-bound). + */ +export const ONLINE_ACCESS_SCOPE = 'online_access'; + +/** + * @ignore + */ +export const DEFAULT_SESSION_CHECK_EXPIRY_DAYS = 1; + +/** + * @ignore + */ +export const DEFAULT_AUTH0_CLIENT = { + name: 'auth0-spa-js', + version: version +}; + +export const DEFAULT_NOW_PROVIDER = () => Date.now(); + +/** @ignore */ +export const SESSION_EXPIRY_LEEWAY_SECONDS = 30; + +export const DEFAULT_AUDIENCE = 'default'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/dpop/dpop.ts b/backend/node_modules/@auth0/auth0-spa-js/src/dpop/dpop.ts new file mode 100644 index 00000000..83c7189a --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/dpop/dpop.ts @@ -0,0 +1,56 @@ +import { DpopStorage } from './storage'; +import * as dpopUtils from './utils'; + +export class Dpop { + protected readonly storage: DpopStorage; + + public constructor(clientId: string) { + this.storage = new DpopStorage(clientId); + } + + public getNonce(id?: string): Promise { + return this.storage.findNonce(id); + } + + public setNonce(nonce: string, id?: string): Promise { + return this.storage.setNonce(nonce, id); + } + + protected async getOrGenerateKeyPair(): Promise { + let keyPair = await this.storage.findKeyPair(); + + if (!keyPair) { + keyPair = await dpopUtils.generateKeyPair(); + await this.storage.setKeyPair(keyPair); + } + + return keyPair; + } + + public async generateProof(params: { + url: string; + method: string; + nonce?: string; + accessToken?: string; + }): Promise { + const keyPair = await this.getOrGenerateKeyPair(); + + return dpopUtils.generateProof({ + keyPair, + ...params + }); + } + + public async calculateThumbprint(): Promise { + const keyPair = await this.getOrGenerateKeyPair(); + + return dpopUtils.calculateThumbprint(keyPair); + } + + public async clear(): Promise { + await Promise.all([ + this.storage.clearNonces(), + this.storage.clearKeyPairs() + ]); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/dpop/storage.ts b/backend/node_modules/@auth0/auth0-spa-js/src/dpop/storage.ts new file mode 100644 index 00000000..c668902a --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/dpop/storage.ts @@ -0,0 +1,136 @@ +import { type KeyPair } from './utils'; + +const VERSION = 1; +const NAME = 'auth0-spa-js'; +const TABLES = { + NONCE: 'nonce', + KEYPAIR: 'keypair' +} as const; + +const AUTH0_NONCE_ID = 'auth0'; + +type Table = (typeof TABLES)[keyof typeof TABLES]; + +export class DpopStorage { + protected readonly clientId: string; + protected dbHandle: IDBDatabase | undefined; + + constructor(clientId: string) { + this.clientId = clientId; + } + + protected getVersion(): number { + return VERSION; + } + + protected createDbHandle(): Promise { + const req = window.indexedDB.open(NAME, this.getVersion()); + + return new Promise((resolve, reject) => { + req.onupgradeneeded = () => + Object.values(TABLES).forEach(t => req.result.createObjectStore(t)); + + req.onerror = () => reject(req.error); + req.onsuccess = () => resolve(req.result); + }); + } + + protected async getDbHandle(): Promise { + if (!this.dbHandle) { + this.dbHandle = await this.createDbHandle(); + } + + return this.dbHandle; + } + + protected async executeDbRequest( + table: string, + mode: IDBTransactionMode, + requestFactory: (table: IDBObjectStore) => IDBRequest + ): Promise { + const db = await this.getDbHandle(); + + const txn = db.transaction(table, mode); + const store = txn.objectStore(table); + + const request = requestFactory(store); + + return new Promise((resolve, reject) => { + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + + protected buildKey(id?: string): string { + const finalId = id + ? `_${id}` // prefix to avoid collisions + : AUTH0_NONCE_ID; + + return `${this.clientId}::${finalId}`; + } + + public setNonce(nonce: string, id?: string): Promise { + return this.save(TABLES.NONCE, this.buildKey(id), nonce); + } + + public setKeyPair(keyPair: KeyPair): Promise { + return this.save(TABLES.KEYPAIR, this.buildKey(), keyPair); + } + + protected async save( + table: Table, + key: IDBValidKey, + obj: unknown + ): Promise { + return void await this.executeDbRequest(table, 'readwrite', table => + table.put(obj, key) + ); + } + + public findNonce(id?: string): Promise { + return this.find(TABLES.NONCE, this.buildKey(id)); + } + + public findKeyPair(): Promise { + return this.find(TABLES.KEYPAIR, this.buildKey()); + } + + protected find( + table: Table, + key: IDBValidKey + ): Promise { + return this.executeDbRequest(table, 'readonly', table => table.get(key)); + } + + protected async deleteBy( + table: Table, + predicate: (key: IDBValidKey) => boolean + ): Promise { + const allKeys = await this.executeDbRequest(table, 'readonly', table => + table.getAllKeys() + ); + + await Promise.all( + allKeys + ?.filter(predicate) + .map(k => + this.executeDbRequest(table, 'readwrite', table => table.delete(k)) + ) || [] + ); + } + + protected deleteByClientId(table: Table, clientId: string): Promise { + return this.deleteBy( + table, + k => typeof k === 'string' && k.startsWith(`${clientId}::`) + ); + } + + public clearNonces(): Promise { + return this.deleteByClientId(TABLES.NONCE, this.clientId); + } + + public clearKeyPairs(): Promise { + return this.deleteByClientId(TABLES.KEYPAIR, this.clientId); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/dpop/utils.ts b/backend/node_modules/@auth0/auth0-spa-js/src/dpop/utils.ts new file mode 100644 index 00000000..0e7059ec --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/dpop/utils.ts @@ -0,0 +1,70 @@ +import * as dpopLib from 'dpop'; + +export const DPOP_NONCE_HEADER = 'dpop-nonce'; + +const KEY_PAIR_ALGORITHM: dpopLib.JWSAlgorithm = 'ES256'; + +const SUPPORTED_GRANT_TYPES = [ + 'authorization_code', + 'refresh_token', + 'urn:ietf:params:oauth:grant-type:token-exchange', + 'urn:okta:params:oauth:grant-type:webauthn', + 'http://auth0.com/oauth/grant-type/mfa-oob', + 'http://auth0.com/oauth/grant-type/mfa-otp', + 'http://auth0.com/oauth/grant-type/mfa-recovery-code' +]; + +export type KeyPair = Readonly; + +type GenerateProofParams = { + keyPair: KeyPair; + url: string; + method: string; + nonce?: string; + accessToken?: string; +}; + +export function generateKeyPair(): Promise { + return dpopLib.generateKeyPair(KEY_PAIR_ALGORITHM, { extractable: false }); +} + +export function calculateThumbprint( + keyPair: Pick +): Promise { + return dpopLib.calculateThumbprint(keyPair.publicKey); +} + +function normalizeUrl(url: string): string { + const parsedUrl = new URL(url); + + /** + * "The HTTP target URI (...) without query and fragment parts" + * @see {@link https://www.rfc-editor.org/rfc/rfc9449.html#section-4.2-4.6} + */ + parsedUrl.search = ''; + parsedUrl.hash = ''; + + return parsedUrl.href; +} + +export function generateProof({ + keyPair, + url, + method, + nonce, + accessToken +}: GenerateProofParams): Promise { + const normalizedUrl = normalizeUrl(url); + + return dpopLib.generateProof( + keyPair, + normalizedUrl, + method, + nonce, + accessToken + ); +} + +export function isGrantTypeSupported(grantType: string): boolean { + return SUPPORTED_GRANT_TYPES.includes(grantType); +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/errors.ts b/backend/node_modules/@auth0/auth0-spa-js/src/errors.ts new file mode 100644 index 00000000..fc4c3262 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/errors.ts @@ -0,0 +1,181 @@ +/** + * MFA requirements from an mfa_required error response + */ +export interface MfaRequirements { + /** Required enrollment types */ + enroll?: Array<{ type: string }>; + /** Required challenge types */ + challenge?: Array<{ type: string }>; +} + +/** + * Thrown when network requests to the Auth server fail. + */ +export class GenericError extends Error { + constructor(public error: string, public error_description: string) { + super(error_description); + Object.setPrototypeOf(this, GenericError.prototype); + } + + static fromPayload({ + error, + error_description + }: { + error: string; + error_description: string; + }) { + return new GenericError(error, error_description); + } +} + +/** + * Thrown at construction time for an invalid option combination (e.g. + * `refreshTokenMode: 'online'` without `useDpop: true`). The `suggestion` names the fix. + */ +export class InvalidConfigurationError extends GenericError { + constructor(message: string, public suggestion: string) { + super('invalid_configuration', `${message} ${suggestion}`); + Object.setPrototypeOf(this, InvalidConfigurationError.prototype); + } +} + +/** + * Thrown when handling the redirect callback fails, will be one of Auth0's + * Authentication API's Standard Error Responses: https://auth0.com/docs/api/authentication?javascript#standard-error-responses + */ +export class AuthenticationError extends GenericError { + constructor( + error: string, + error_description: string, + public state: string, + public appState: any = null + ) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, AuthenticationError.prototype); + } +} + +/** + * Thrown when handling the redirect callback for the connect flow fails, will be one of Auth0's + * Authentication API's Standard Error Responses: https://auth0.com/docs/api/authentication?javascript#standard-error-responses + */ +export class ConnectError extends GenericError { + constructor( + error: string, + error_description: string, + public connection: string, + public state: string, + public appState: any = null + ) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, ConnectError.prototype); + } +} + +/** + * Thrown when silent auth times out (usually due to a configuration issue) or + * when network requests to the Auth server timeout. + */ +export class TimeoutError extends GenericError { + constructor() { + super('timeout', 'Timeout'); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, TimeoutError.prototype); + } +} + +/** + * Error thrown when the login popup times out (if the user does not complete auth) + */ +export class PopupTimeoutError extends TimeoutError { + constructor(public popup: Window) { + super(); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, PopupTimeoutError.prototype); + } +} + +export class PopupCancelledError extends GenericError { + constructor(public popup: Window) { + super('cancelled', 'Popup closed'); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, PopupCancelledError.prototype); + } +} + +export class PopupOpenError extends GenericError { + constructor() { + super('popup_open', 'Unable to open a popup for loginWithPopup - window.open returned `null`'); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, PopupOpenError.prototype); + } +} + +/** + * Error thrown when the token exchange results in a `mfa_required` error + */ +export class MfaRequiredError extends GenericError { + constructor( + error: string, + error_description: string, + public mfa_token: string, + public mfa_requirements: MfaRequirements + ) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaRequiredError.prototype); + } +} + +/** + * Error thrown when there is no refresh token to use + */ +export class MissingRefreshTokenError extends GenericError { + constructor(public audience: string, public scope: string) { + super( + 'missing_refresh_token', + `Missing Refresh Token (audience: '${valueOrEmptyString(audience, [ + 'default' + ])}', scope: '${valueOrEmptyString(scope)}')` + ); + Object.setPrototypeOf(this, MissingRefreshTokenError.prototype); + } +} + +/** + * Error thrown when there are missing scopes after refreshing a token + */ +export class MissingScopesError extends GenericError { + constructor(public audience: string, public scope: string) { + super( + 'missing_scopes', + `Missing requested scopes after refresh (audience: '${valueOrEmptyString(audience, [ + 'default' + ])}', missing scope: '${valueOrEmptyString(scope)}')` + ); + Object.setPrototypeOf(this, MissingScopesError.prototype); + } +} + +/** + * Error thrown when the wrong DPoP nonce is used and a potential subsequent retry wasn't able to fix it. + */ +export class UseDpopNonceError extends GenericError { + constructor(public newDpopNonce: string | undefined) { + super('use_dpop_nonce', 'Server rejected DPoP proof: wrong nonce'); + + Object.setPrototypeOf(this, UseDpopNonceError.prototype); + } +} + +/** + * Returns an empty string when value is falsy, or when it's value is included in the exclude argument. + * @param value The value to check + * @param exclude An array of values that should result in an empty string. + * @returns The value, or an empty string when falsy or included in the exclude argument. + */ +function valueOrEmptyString(value: string, exclude: string[] = []) { + return value && !exclude.includes(value) ? value : ''; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/fetcher.ts b/backend/node_modules/@auth0/auth0-spa-js/src/fetcher.ts new file mode 100644 index 00000000..5b7ff234 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/fetcher.ts @@ -0,0 +1,271 @@ +import { DPOP_NONCE_HEADER } from './dpop/utils'; +import { UseDpopNonceError } from './errors'; +import { GetTokenSilentlyVerboseResponse } from './global'; + +export type ResponseHeaders = + | Record + | [string, string][] + | { get(name: string): string | null | undefined }; + +export type CustomFetchMinimalOutput = { + status: number; + headers: ResponseHeaders; +}; + +export type CustomFetchImpl = ( + req: Request +) => Promise; + +export type AuthParams = { + scope?: string[]; + audience?: string; +}; + +type AccessTokenFactory = (authParams?: AuthParams) => Promise; + +enum TokenType { + Bearer = 'Bearer', + DPoP = 'DPoP' +} + +export type FetcherConfig = { + getAccessToken?: AccessTokenFactory; + baseUrl?: string; + fetch?: CustomFetchImpl; + dpopNonceId?: string; +}; + +export type FetcherHooks = { + isDpopEnabled: () => boolean; + getAccessToken: AccessTokenFactory; + getDpopNonce: () => Promise; + setDpopNonce: (nonce: string) => Promise; + generateDpopProof: (params: { + url: string; + method: string; + nonce?: string; + accessToken: string; + }) => Promise; +}; + +export type FetchWithAuthCallbacks = { + onUseDpopNonceError?(): Promise; +}; + +export class Fetcher { + protected readonly config: Omit, 'fetch'> & + Required, 'fetch'>>; + + protected readonly hooks: FetcherHooks; + + constructor(config: FetcherConfig, hooks: FetcherHooks) { + this.hooks = hooks; + + this.config = { + ...config, + fetch: + config.fetch || + // For easier testing and constructor compatibility with SSR. + ((typeof window === 'undefined' + ? fetch + : window.fetch.bind(window)) as () => Promise) + }; + } + + protected isAbsoluteUrl(url: string): boolean { + // `http://example.com`, `https://example.com` or `//example.com` + return /^(https?:)?\/\//i.test(url); + } + + protected buildUrl( + baseUrl: string | undefined, + url: string | undefined + ): string { + if (url) { + if (this.isAbsoluteUrl(url)) { + return url; + } + + if (baseUrl) { + return `${baseUrl.replace(/\/?\/$/, '')}/${url.replace(/^\/+/, '')}`; + } + } + + throw new TypeError('`url` must be absolute or `baseUrl` non-empty.'); + } + + protected getAccessToken(authParams?: AuthParams): Promise { + return this.config.getAccessToken + ? this.config.getAccessToken(authParams) + : this.hooks.getAccessToken(authParams); + } + + protected extractUrl(info: RequestInfo | URL): string { + if (typeof info === 'string') { + return info; + } + + if (info instanceof URL) { + return info.href; + } + + return info.url; + } + + protected buildBaseRequest( + info: RequestInfo | URL, + init: RequestInit | undefined + ): Request { + // In the native `fetch()` behavior, `init` can override `info` and the result + // is the merge of both. So let's replicate that behavior by passing those into + // a fresh `Request` object. + + // No `baseUrl`? We can use `info` and `init` as is. + if (!this.config.baseUrl) { + return new Request(info, init); + } + + // But if `baseUrl` is present, first we have to build the final URL... + const finalUrl = this.buildUrl(this.config.baseUrl, this.extractUrl(info)); + + // ... and then overwrite `info`'s URL with it, making sure we keep any other + // properties that might be there already (headers, etc). + const finalInfo = info instanceof Request + ? new Request(finalUrl, info) + : finalUrl; + + return new Request(finalInfo, init); + } + + protected setAuthorizationHeader( + request: Request, + accessToken: string, + tokenType: string = TokenType.Bearer + ): void { + request.headers.set( + 'authorization', + `${tokenType} ${accessToken}` + ); + } + + protected async setDpopProofHeader( + request: Request, + accessToken: string + ): Promise { + if (!this.config.dpopNonceId) { + return; + } + + const dpopNonce = await this.hooks.getDpopNonce(); + + const dpopProof = await this.hooks.generateDpopProof({ + accessToken, + method: request.method, + nonce: dpopNonce, + url: request.url + }); + + request.headers.set('dpop', dpopProof); + } + + protected async prepareRequest(request: Request, authParams?: AuthParams) { + const accessTokenResponse = await this.getAccessToken(authParams); + + let tokenType: string; + let accessToken: string; + if (typeof accessTokenResponse === 'string') { + tokenType = this.config.dpopNonceId ? TokenType.DPoP : TokenType.Bearer; + accessToken = accessTokenResponse; + } else { + tokenType = accessTokenResponse.token_type; + accessToken = accessTokenResponse.access_token; + } + + this.setAuthorizationHeader(request, accessToken, tokenType); + if (tokenType === TokenType.DPoP) { + await this.setDpopProofHeader(request, accessToken); + } + } + + protected getHeader(headers: ResponseHeaders, name: string): string { + if (Array.isArray(headers)) { + return new Headers(headers).get(name) || ''; + } + + if (typeof headers.get === 'function') { + return headers.get(name) || ''; + } + + return (headers as Record)[name] || ''; + } + + protected hasUseDpopNonceError(response: TOutput): boolean { + if (response.status !== 401) { + return false; + } + + const wwwAuthHeader = this.getHeader(response.headers, 'www-authenticate'); + + return wwwAuthHeader.includes('invalid_dpop_nonce') || wwwAuthHeader.includes('use_dpop_nonce'); + } + + protected async handleResponse( + response: TOutput, + callbacks: FetchWithAuthCallbacks + ): Promise { + const newDpopNonce = this.getHeader(response.headers, DPOP_NONCE_HEADER); + + if (newDpopNonce) { + await this.hooks.setDpopNonce(newDpopNonce); + } + + if (!this.hasUseDpopNonceError(response)) { + return response; + } + + // After a `use_dpop_nonce` error, if we didn't get a new DPoP nonce or we + // did but it still got rejected for the same reason, we have to give up. + if (!newDpopNonce || !callbacks.onUseDpopNonceError) { + throw new UseDpopNonceError(newDpopNonce); + } + + return callbacks.onUseDpopNonceError(); + } + + protected async internalFetchWithAuth( + info: RequestInfo | URL, + init: RequestInit | undefined, + callbacks: FetchWithAuthCallbacks, + authParams?: AuthParams + ): Promise { + const request = this.buildBaseRequest(info, init); + + await this.prepareRequest(request, authParams); + + const response = await this.config.fetch(request); + + return this.handleResponse(response, callbacks); + } + + public fetchWithAuth( + info: RequestInfo | URL, + init?: RequestInit, + authParams?: AuthParams + ): Promise { + const callbacks: FetchWithAuthCallbacks = { + onUseDpopNonceError: () => + this.internalFetchWithAuth( + info, + init, + { + ...callbacks, + // Retry on a `use_dpop_nonce` error, but just once. + onUseDpopNonceError: undefined + }, + authParams + ) + }; + + return this.internalFetchWithAuth(info, init, callbacks, authParams); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/global.ts b/backend/node_modules/@auth0/auth0-spa-js/src/global.ts new file mode 100644 index 00000000..3710a657 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/global.ts @@ -0,0 +1,988 @@ +import { ICache } from './cache'; +import type { Dpop } from './dpop/dpop'; +import { CompleteResponse } from './myaccount'; + +/** + * Configuration option for automatic interactive error handling. + * + * - `'popup'`: SDK automatically opens Universal Login popup on MFA error + */ +export type InteractiveErrorHandler = 'popup'; + +/** + * The refresh-token variant used when `useRefreshTokens: true`. + * + * - {@link RefreshTokenMode.Offline} (default): rotating offline refresh tokens. + * - {@link RefreshTokenMode.Online}: non-rotating Online Refresh Tokens (ORTs). + */ +// `const` object, not a TS `enum`, so members stay string-literal types and +// remain assignable to the `createAuth0Client` overloads. +export const RefreshTokenMode = { + Offline: 'offline', + Online: 'online' +} as const; + +export type RefreshTokenMode = + (typeof RefreshTokenMode)[keyof typeof RefreshTokenMode]; + +export interface AuthorizationParams { + /** + * - `'page'`: displays the UI with a full page view + * - `'popup'`: displays the UI with a popup window + * - `'touch'`: displays the UI in a way that leverages a touch interface + * - `'wap'`: displays the UI with a "feature phone" type interface + */ + display?: 'page' | 'popup' | 'touch' | 'wap'; + + /** + * - `'none'`: do not prompt user for login or consent on reauthentication + * - `'login'`: prompt user for reauthentication + * - `'consent'`: prompt user for consent before processing request + * - `'select_account'`: prompt user to select an account + */ + prompt?: 'none' | 'login' | 'consent' | 'select_account'; + + /** + * Maximum allowable elapsed time (in seconds) since authentication. + * If the last time the user authenticated is greater than this value, + * the user must be reauthenticated. + */ + max_age?: string | number; + + /** + * The space-separated list of language tags, ordered by preference. + * For example: `'fr-CA fr en'`. + */ + ui_locales?: string; + + /** + * Previously issued ID Token. + */ + id_token_hint?: string; + + /** + * Provides a hint to Auth0 as to what flow should be displayed. + * The default behavior is to show a login page but you can override + * this by passing 'signup' to show the signup page instead. + * + * This only affects the New Universal Login Experience. + */ + screen_hint?: 'signup' | 'login' | string; + + /** + * The user's email address or other identifier. When your app knows + * which user is trying to authenticate, you can provide this parameter + * to pre-fill the email box or select the right session for sign-in. + * + * This currently only affects the classic Lock experience. + */ + login_hint?: string; + + acr_values?: string; + + /** + * The default scope to be used on authentication requests. + * + * This defaults to `profile email` if not set. If you are setting extra scopes and require + * `profile` and `email` to be included then you must include them in the provided scope. + * + * Note: The `openid` scope is **always applied** regardless of this setting. + */ + scope?: string; + + /** + * The default audience to be used for requesting API access. + */ + audience?: string; + + /** + * The name of the connection configured for your application. + * If null, it will redirect to the Auth0 Login Page and show + * the Login Widget. + */ + connection?: string; + + /** + * The organization to log in to. + * + * This will specify an `organization` parameter in your user's login request. + * + * - If you provide an Organization ID (a string with the prefix `org_`), it will be validated against the `org_id` claim of your user's ID Token. The validation is case-sensitive. + * - If you provide an Organization Name (a string *without* the prefix `org_`), it will be validated against the `org_name` claim of your user's ID Token. The validation is case-insensitive. + * To use an Organization Name you must have "Allow Organization Names in Authentication API" switched on in your Auth0 settings dashboard. + * More information is available on the [Auth0 documentation portal](https://auth0.com/docs/manage-users/organizations/configure-organizations/use-org-name-authentication-api) + * + */ + organization?: string; + + /** + * The Id of an invitation to accept. This is available from the user invitation URL that is given when participating in a user invitation flow. + */ + invitation?: string; + + /** + * The default URL where Auth0 will redirect your browser to with + * the authentication result. It must be whitelisted in + * the "Allowed Callback URLs" field in your Auth0 Application's + * settings. If not provided here, it should be provided in the other + * methods that provide authentication. + */ + redirect_uri?: string; + + /** + * Session transfer token from a native application for Native to Web SSO. + * When `sessionTransferTokenQueryParamName` is set, this is automatically + * extracted from the specified URL query parameter if present. + * + * @see https://auth0.com/docs/authenticate/single-sign-on/native-to-web + */ + session_transfer_token?: string; + + /** + * If you need to send custom parameters to the Authorization Server, + * make sure to use the original parameter name. + */ + [key: string]: any; +} + +export interface ClientAuthorizationParams extends Omit { + scope?: string | Record +}; + +interface BaseLoginOptions { + /** + * URL parameters that will be sent back to the Authorization Server. This can be known parameters + * defined by Auth0 or custom parameters that you define. + */ + authorizationParams?: AuthorizationParams; +} + +export interface Auth0ClientOptions { + /** + * Your Auth0 account domain such as `'example.auth0.com'`, + * `'example.eu.auth0.com'` or , `'example.mycompany.com'` + * (when using [custom domains](https://auth0.com/docs/custom-domains)) + */ + domain: string; + /** + * The issuer to be used for validation of JWTs, optionally defaults to the domain above + */ + issuer?: string; + /** + * The Client ID found on your Application settings page + */ + clientId: string; + /** + * The value in seconds used to account for clock skew in JWT expirations. + * Typically, this value is no more than a minute or two at maximum. + * Defaults to 60s. + */ + leeway?: number; + + /** + * The location to use when storing cache data. Valid values are `memory` or `localstorage`. + * The default setting is `memory`. + * + * Read more about [changing storage options in the Auth0 docs](https://auth0.com/docs/libraries/auth0-single-page-app-sdk#change-storage-options) + */ + cacheLocation?: CacheLocation; + + /** + * Specify a custom cache implementation to use for token storage and retrieval. This setting takes precedence over `cacheLocation` if they are both specified. + */ + cache?: ICache; + + /** + * If true, refresh tokens are used to fetch new access tokens from the Auth0 server. If false, the standard technique of using a hidden iframe and the `authorization_code` grant with `prompt=none` is used. + * The default setting is `false`. + * + * Standard technique relies on cookies. Because browsers increasingly block third-party cookies, it requires a Custom Domain to function reliably. Refresh tokens serve as a fallback for environments where third-party cookies are blocked. + * Using a Custom Domain with this set to `false` is the most secure and recommended approach. + * + * **Note**: Use of refresh tokens must be enabled by an administrator on your Auth0 client application. + */ + useRefreshTokens?: boolean; + + /** + * If true, fallback to the technique of using a hidden iframe and the `authorization_code` grant with `prompt=none` when unable to use refresh tokens. If false, the iframe fallback is not used and + * errors relating to a failed `refresh_token` grant should be handled appropriately. The default setting is `false`. + * + * **Note**: There might be situations where doing silent auth with a Web Message response from an iframe is not possible, + * like when you're serving your application from the file system or a custom protocol (like in a Desktop or Native app). + * In situations like this you can disable the iframe fallback and handle the failed `refresh_token` grant and prompt the user to login interactively with `loginWithRedirect` or `loginWithPopup`." + * + * E.g. Using the `file:` protocol in an Electron application does not support that legacy technique. + * + * @example + * let token: string; + * try { + * token = await auth0.getTokenSilently(); + * } catch (e) { + * if (e.error === 'missing_refresh_token' || e.error === 'invalid_grant') { + * auth0.loginWithRedirect(); + * } + * } + */ + useRefreshTokensFallback?: boolean; + + /** + * A maximum number of seconds to wait before declaring background calls to /authorize as failed for timeout + * Defaults to 60s. + */ + authorizeTimeoutInSeconds?: number; + + /** + * Specify the timeout for HTTP calls using `fetch`. The default is 10 seconds. + */ + httpTimeoutInSeconds?: number; + + /** + * Internal property to send information about the client to the authorization server. + * @internal + */ + auth0Client?: { + name: string; + version: string; + env?: { [key: string]: string }; + }; + + /** + * Sets an additional cookie with no SameSite attribute to support legacy browsers + * that are not compatible with the latest SameSite changes. + * This will log a warning on modern browsers, you can disable the warning by setting + * this to false but be aware that some older useragents will not work, + * See https://www.chromium.org/updates/same-site/incompatible-clients + * Defaults to true + */ + legacySameSiteCookie?: boolean; + + /** + * If `true`, the SDK will use a cookie when storing information about the auth transaction while + * the user is going through the authentication flow on the authorization server. + * + * The default is `false`, in which case the SDK will use session storage. + * + * @notes + * + * You might want to enable this if you rely on your users being able to authenticate using flows that + * may end up spanning across multiple tabs (e.g. magic links) or you cannot otherwise rely on session storage being available. + */ + useCookiesForTransactions?: boolean; + + /** + * Number of days until the cookie `auth0.is.authenticated` will expire + * Defaults to 1. + */ + sessionCheckExpiryDays?: number; + + /** + * The domain the cookie is accessible from. If not set, the cookie is scoped to + * the current domain, including the subdomain. + * + * Note: setting this incorrectly may cause silent authentication to stop working + * on page load. + * + * + * To keep a user logged in across multiple subdomains set this to your + * top-level domain and prefixed with a `.` (eg: `.example.com`). + */ + cookieDomain?: string; + + /** + * If true, data to the token endpoint is transmitted as x-www-form-urlencoded data, if false it will be transmitted as JSON. The default setting is `true`. + * + * **Note:** Setting this to `false` may affect you if you use Auth0 Rules and are sending custom, non-primitive data. If you disable this, + * please verify that your Auth0 Rules continue to work as intended. + */ + useFormData?: boolean; + + /** + * Modify the value used as the current time during the token validation. + * + * **Note**: Using this improperly can potentially compromise the token validation. + */ + nowProvider?: () => Promise | number; + + /** + * If provided, the SDK will load the token worker from this URL instead of the integrated `blob`. An example of when this is useful is if you have strict + * Content-Security-Policy (CSP) and wish to avoid needing to set `worker-src: blob:`. We recommend either serving the worker, which you can find in the module + * at `/dist/auth0-spa-js.worker.production.js`, from the same host as your application or using the Auth0 CDN + * `https://cdn.auth0.com/js/auth0-spa-js//auth0-spa-js.worker.production.js`. + * + * **Note**: The worker is only used when `useRefreshTokens: true`, `cacheLocation: 'memory'`, and the `cache` is not custom. + */ + workerUrl?: string; + + + /** + * If `true`, the SDK will allow the refreshing of tokens using MRRT + */ + useMrrt?: boolean; + + /** + * If `true`, DPoP (OAuth 2.0 Demonstrating Proof of Possession, RFC9449) + * will be used to cryptographically bind tokens to this specific browser + * so they can't be used from a different device in case of a leak. + * + * The default setting is `false`. + */ + useDpop?: boolean; + + /** + * Selects the refresh-token variant used when `useRefreshTokens: true`. + * Defaults to {@link RefreshTokenMode.Offline}. + * + * - {@link RefreshTokenMode.Offline} (default): rotating offline refresh tokens (`offline_access`). + * - {@link RefreshTokenMode.Online}: non-rotating, session-bound Online Refresh Tokens (`online_access`). + * + * Online mode requires `useRefreshTokens: true` and `useDpop: true`. The + * `online_access` and `offline_access` scopes are mutually exclusive. + */ + refreshTokenMode?: RefreshTokenMode; + + /** + * Configures automatic handling of interactive authentication errors. + * + * When set, the SDK intercepts `mfa_required` errors from `getTokenSilently()` + * and handles them automatically instead of throwing to the caller. + * + * - `'popup'`: Opens Universal Login in a popup to complete MFA. + * The original `authorizationParams` (audience, scope) are preserved. + * On success, the token is returned. On failure, popup errors are thrown. + * + * This option only affects `getTokenSilently()`. Other methods are not affected. + * + * @default undefined (MFA errors are thrown to the caller) + */ + interactiveErrorHandler?: InteractiveErrorHandler; + + /** + * URL parameters that will be sent back to the Authorization Server. This can be known parameters + * defined by Auth0 or custom parameters that you define. + */ + authorizationParams?: ClientAuthorizationParams; + + /** + * Query parameter name to extract the session transfer token from for Native to Web SSO. + * + * When set, the SDK automatically extracts the token from the specified URL query + * parameter and includes it as `session_transfer_token` in authorization requests. + * This enables seamless single sign-on when users transition from a native mobile + * application to a web application. + * + * After extraction, the token is automatically removed from the URL using + * `window.history.replaceState()` to prevent accidental reuse on subsequent + * authentication requests. + * + * **Default:** `undefined` (feature disabled) + * + * **Common values:** + * - `'session_transfer_token'` - Standard parameter name + * - `'stt'` - Shortened version + * - Custom parameter name of your choice + * + * Set to `undefined` to disable automatic extraction if you prefer to handle + * session transfer tokens manually. + * + * @example + * ```js + * const auth0 = await createAuth0Client({ + * domain: '', + * clientId: '', + * sessionTransferTokenQueryParamName: 'session_transfer_token' + * }); + * ``` + * + * @see https://auth0.com/docs/authenticate/single-sign-on/native-to-web + */ + sessionTransferTokenQueryParamName?: string; +} + +/** + * Configuration details exposed by the Auth0Client after initialization. + * + * @category Main + */ +export interface ClientConfiguration { + /** + * The Auth0 domain that was configured + */ + domain: string; + + /** + * The Auth0 client ID that was configured + */ + clientId: string; +} + +/** + * The possible locations where tokens can be stored + */ +export type CacheLocation = 'memory' | 'localstorage'; + +/** + * @ignore + */ +export interface AuthorizeOptions extends AuthorizationParams { + response_type: string; + response_mode: string; + redirect_uri?: string; + nonce: string; + state: string; + scope: string; + code_challenge: string; + code_challenge_method: string; +} + +export interface RedirectLoginOptions + extends BaseLoginOptions { + /** + * Used to store state before doing the redirect + */ + appState?: TAppState; + /** + * Used to add to the URL fragment before redirecting + */ + fragment?: string; + /** + * Used to control the redirect and not rely on the SDK to do the actual redirect. + * + * @example + * const client = new Auth0Client({ + * async onRedirect(url) { + * window.location.replace(url); + * } + * }); + * @deprecated since v2.0.1, use `openUrl` instead. + */ + onRedirect?: (url: string) => Promise; + + /** + * Used to control the redirect and not rely on the SDK to do the actual redirect. + * + * @example + * const client = new Auth0Client({ + * openUrl(url) { + * window.location.replace(url); + * } + * }); + * + * @example + * import { Browser } from '@capacitor/browser'; + * + * const client = new Auth0Client({ + * async openUrl(url) { + * await Browser.open({ url }); + * } + * }); + */ + openUrl?: (url: string) => Promise | void; +} + +/** + * The types of responses expected from the authorization server. + * - `code`: used for the standard login flow. + * - `connect_code`: used for the connect account flow. + */ +export enum ResponseType { + Code = 'code', + ConnectCode = 'connect_code' +} + +export interface RedirectLoginResult { + /** + * State stored when the redirect request was made + */ + appState?: TAppState; + + /** + * The type of response, for login it will be `code` + */ + response_type: ResponseType.Code; +} + +export interface PopupLoginOptions extends BaseLoginOptions { } + +export interface PopupConfigOptions { + /** + * The number of seconds to wait for a popup response before + * throwing a timeout error. Defaults to 60s + */ + timeoutInSeconds?: number; + + /** + * Accepts an already-created popup window to use. If not specified, the SDK + * will create its own. This may be useful for platforms like iOS that have + * security restrictions around when popups can be invoked (e.g. from a user click event) + */ + popup?: any; + + /** + * Controls whether the SDK automatically closes the popup window. + * + * - `true` (default): SDK closes the popup automatically after receiving the authorization response + * - `false`: SDK does not close the popup. The caller is responsible for closing it, including on errors. + * + * Setting this to `false` is useful when you need full control over the popup lifecycle, + * such as in Chrome extensions where closing the popup too early can terminate the + * extension's service worker before authentication completes. + * + * When `closePopup: false`, you should close the popup in a try/finally block: + * ``` + * const popup = window.open('', '_blank'); + * try { + * await auth0.loginWithPopup({}, { popup, closePopup: false }); + * } finally { + * popup.close(); + * } + * ``` + * + * @default true + */ + closePopup?: boolean; +} + +export interface GetTokenSilentlyOptions { + /** + * When `off`, ignores the cache and always sends a + * request to Auth0. Concurrent `off` calls are not deduplicated — each makes its own request. + * When `cache-only`, only reads from the cache and never sends a request to Auth0. + * Defaults to `on`, where it both reads from the cache and sends a request to Auth0 as needed. + */ + cacheMode?: 'on' | 'off' | 'cache-only'; + + /** + * Parameters that will be sent back to Auth0 as part of a request. + */ + authorizationParams?: { + /** + * There's no actual redirect when getting a token silently, + * but, according to the spec, a `redirect_uri` param is required. + * Auth0 uses this parameter to validate that the current `origin` + * matches the `redirect_uri` `origin` when sending the response. + * It must be whitelisted in the "Allowed Web Origins" in your + * Auth0 Application's settings. + */ + redirect_uri?: string; + + /** + * The scope that was used in the authentication request + */ + scope?: string; + + /** + * The audience that was used in the authentication request + */ + audience?: string; + + /** + * If you need to send custom parameters to the Authorization Server, + * make sure to use the original parameter name. + */ + [key: string]: any; + }; + + /** A maximum number of seconds to wait before declaring the background /authorize call as failed for timeout + * Defaults to 60s. + */ + timeoutInSeconds?: number; + + /** + * If true, the full response from the /oauth/token endpoint (or the cache, if the cache was used) is returned + * (minus `refresh_token` if one was issued). Otherwise, just the access token is returned. + * + * The default is `false`. + */ + detailedResponse?: boolean; +} + +export interface GetTokenWithPopupOptions extends PopupLoginOptions { + /** + * When `off`, ignores the cache and always sends a request to Auth0. + * When `cache-only`, only reads from the cache and never sends a request to Auth0. + * Defaults to `on`, where it both reads from the cache and sends a request to Auth0 as needed. + */ + cacheMode?: 'on' | 'off' | 'cache-only'; +} + +export interface LogoutUrlOptions { + /** + * The `clientId` of your application. + * + * If this property is not set, then the `clientId` that was used during initialization of the SDK is sent to the logout endpoint. + * + * If this property is set to `null`, then no client ID value is sent to the logout endpoint. + * + * [Read more about how redirecting after logout works](https://auth0.com/docs/logout/guides/redirect-users-after-logout) + */ + clientId?: string | null; + + /** + * Parameters to pass to the logout endpoint. This can be known parameters defined by Auth0 or custom parameters + * you wish to provide. + */ + logoutParams?: { + /** + * When supported by the upstream identity provider, + * forces the user to logout of their identity provider + * and from Auth0. + * [Read more about how federated logout works at Auth0](https://auth0.com/docs/logout/guides/logout-idps) + */ + federated?: boolean; + /** + * The URL where Auth0 will redirect your browser to after the logout. + * + * **Note**: If the `client_id` parameter is included, the + * `returnTo` URL that is provided must be listed in the + * Application's "Allowed Logout URLs" in the Auth0 dashboard. + * However, if the `client_id` parameter is not included, the + * `returnTo` URL must be listed in the "Allowed Logout URLs" at + * the account level in the Auth0 dashboard. + * + * [Read more about how redirecting after logout works](https://auth0.com/docs/logout/guides/redirect-users-after-logout) + */ + returnTo?: string; + + /** + * If you need to send custom parameters to the logout endpoint, make sure to use the original parameter name. + */ + [key: string]: any; + }; +} + +export interface LogoutOptions extends LogoutUrlOptions { + /** + * Used to control the redirect and not rely on the SDK to do the actual redirect. + * + * @example + * await auth0.logout({ + * async onRedirect(url) { + * window.location.replace(url); + * } + * }); + * @deprecated since v2.0.1, use `openUrl` instead. + */ + onRedirect?: (url: string) => Promise; + + /** + * Used to control the redirect and not rely on the SDK to do the actual redirect. + * + * Set to `false` to disable the redirect, or provide a function to handle the actual redirect yourself. + * + * @example + * await auth0.logout({ + * openUrl(url) { + * window.location.replace(url); + * } + * }); + * + * @example + * import { Browser } from '@capacitor/browser'; + * + * await auth0.logout({ + * async openUrl(url) { + * await Browser.open({ url }); + * } + * }); + */ + openUrl?: false | ((url: string) => Promise | void); +} + +export interface RedirectConnectAccountOptions { + /** + * The name of the connection to link (e.g. 'google-oauth2'). + */ + connection: string; + + /** + * Array of scopes to request from the Identity Provider during the connect account flow. + * + * @example + * await auth0.connectAccountWithRedirect({ + * connection: 'google-oauth2', + * scopes: ['https://www.googleapis.com/auth/calendar'] + * }); + */ + scopes?: string[]; + + /** + * Additional authorization parameters for the request. + * + * @example + * await auth0.connectAccountWithRedirect({ + * connection: 'github', + * authorization_params: { + * audience: 'https://api.github.com' + * } + * }); + */ + authorization_params?: AuthorizationParams; + + /** + * The URI to redirect back to after connecting the account. + */ + redirectUri?: string; + + /** + * Optional application state to persist through the transaction. + * + * @example + * await auth0.connectAccountWithRedirect({ + * connection: 'google-oauth2', + * appState: { returnTo: '/settings' } + * }); + */ + appState?: TAppState; + + /** + * Optional function to handle the redirect URL. + * + * @example + * await auth0.connectAccountWithRedirect({ + * connection: 'google-oauth2', + * openUrl: async (url) => { myBrowserApi.open(url); } + * }); + */ + openUrl?: (url: string) => Promise; +} + +/** + * The result returned after a successful account connection redirect. + * + * Combines the redirect login result (including any persisted app state) + * with the complete response from the My Account API. + * + * @template TAppState - The type of application state persisted through the transaction. + * @example + * const result = await auth0.connectAccountWithRedirect(options); + * console.log(result.appState); // Access persisted app state + * console.log(result.connection); // The connection of the account you connected to. + * console.log(result.response_type === 'connect_code'); // The response type will be 'connect_code' + */ +export type ConnectAccountRedirectResult = CompleteResponse & { + /** + * State stored when the redirect request was made + */ + appState?: TAppState; + + /** + * The type of response, for connect account it will be `connect_code` + */ + response_type: ResponseType.ConnectCode; +}; + +/** + * @ignore + */ +export interface AuthenticationResult { + state: string; + code?: string; + /** + * This is for the redirect from the connect account flow. + */ + connect_code?: string; + error?: string; + error_description?: string; +} + +/** + * @ignore + */ +export interface TokenEndpointOptions { + baseUrl: string; + client_id: string; + grant_type: string; + timeout?: number; + auth0Client: any; + useFormData?: boolean; + dpop?: Pick; + [key: string]: any; +} + +export type TokenEndpointResponse = { + id_token: string; + token_type: string; + access_token: string; + refresh_token?: string; + expires_in: number; + scope?: string; +}; + +/** + * @ignore + */ +export interface OAuthTokenOptions extends TokenEndpointOptions { + code_verifier: string; + code: string; + redirect_uri: string; + audience: string; + scope: string; +} + +/** + * @ignore + */ +export interface RefreshTokenOptions extends TokenEndpointOptions { + refresh_token: string; +} + +/** + * @ignore + */ +export interface JWTVerifyOptions { + iss: string; + aud: string; + id_token: string; + nonce?: string; + leeway?: number; + max_age?: number; + organization?: string; + now?: number; +} + +/** + * Represents the actor claim (`act`) in an ID token returned via a token exchange response. + * + * The `act` claim identifies the acting party — the entity that has been delegated authority + * to act on behalf of the subject. It is set via Auth0 Actions using the `setActor` command. + * + * @see {@link https://www.rfc-editor.org/rfc/rfc8693#section-4.1 | RFC 8693 Section 4.1} + */ +export interface ActClaim { + /** The identifier of the acting party. */ + sub?: string; + [key: string]: any; +} + +export interface IdToken { + __raw: string; + name?: string; + given_name?: string; + family_name?: string; + middle_name?: string; + nickname?: string; + preferred_username?: string; + profile?: string; + picture?: string; + website?: string; + email?: string; + email_verified?: boolean; + gender?: string; + birthdate?: string; + zoneinfo?: string; + locale?: string; + phone_number?: string; + phone_number_verified?: boolean; + address?: string; + updated_at?: string; + iss?: string; + aud?: string; + exp?: number; + nbf?: number; + iat?: number; + jti?: string; + azp?: string; + nonce?: string; + auth_time?: string; + at_hash?: string; + c_hash?: string; + acr?: string; + amr?: string[]; + sub_jwk?: string; + cnf?: string; + sid?: string; + org_id?: string; + org_name?: string; + /** + * IPSIE session expiry ceiling (Unix timestamp in seconds). + * When present, the SDK will not return tokens after this point in time. + */ + session_expiry?: number; + /** + * The actor claim, present in ID tokens returned via token exchange responses. + * Identifies the acting party that has been delegated authority to act on behalf + * of the subject. Set via Auth0 Actions using the `setActor` command. + */ + act?: ActClaim; + [key: string]: any; +} + +export class User { + name?: string; + given_name?: string; + family_name?: string; + middle_name?: string; + nickname?: string; + preferred_username?: string; + profile?: string; + picture?: string; + website?: string; + email?: string; + email_verified?: boolean; + gender?: string; + birthdate?: string; + zoneinfo?: string; + locale?: string; + phone_number?: string; + phone_number_verified?: boolean; + address?: string; + updated_at?: string; + sub?: string; + act?: ActClaim; + [key: string]: any; +} + +/** + * @ignore + */ +export type FetchOptions = { + method?: string; + headers?: Record; + credentials?: 'include' | 'omit'; + body?: string; + signal?: AbortSignal; +}; + +/** + * @ignore + */ +export type FetchResponse = { + ok: boolean; + headers: Record; + json: any; +}; + +export type GetTokenSilentlyVerboseResponse = Omit< + TokenEndpointResponse, + 'refresh_token' +>; + +/** + * Options for revoking a refresh token + */ +export interface RevokeRefreshTokenOptions { + /** Audience to identify which refresh token to revoke. Omit for default audience. */ + audience?: string; +} + +// MFA API types +export type { + Authenticator, + AuthenticatorType, + OobChannel, + MfaFactorType, + EnrollParams, + EnrollOtpParams, + EnrollSmsParams, + EnrollVoiceParams, + EnrollEmailParams, + EnrollPushParams, + EnrollmentResponse, + OtpEnrollmentResponse, + OobEnrollmentResponse, + ChallengeAuthenticatorParams, + ChallengeResponse, + VerifyParams, + MfaGrantType, + EnrollmentFactor +} from './mfa/types'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/http.ts b/backend/node_modules/@auth0/auth0-spa-js/src/http.ts new file mode 100644 index 00000000..72f530bf --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/http.ts @@ -0,0 +1,250 @@ +import { + DEFAULT_FETCH_TIMEOUT_MS, + DEFAULT_SILENT_TOKEN_RETRY_COUNT +} from './constants'; + +import { fromEntries } from './utils'; +import { sendMessage } from './worker/worker.utils'; +import { FetchOptions, FetchResponse } from './global'; +import { + GenericError, + MfaRequiredError, + MissingRefreshTokenError, + UseDpopNonceError +} from './errors'; +import { Dpop } from './dpop/dpop'; +import { DPOP_NONCE_HEADER } from './dpop/utils'; + +export const createAbortController = () => new AbortController(); + +/** + * Wraps a single `fetch` call with an AbortController-based timeout and + * returns the raw `Response`. Shared by the JSON token path and the revoke + * path to avoid duplicating abort/timeout orchestration. + */ +export const fetchWithTimeout = ( + fetchUrl: string, + fetchOptions: FetchOptions, + timeout: number +): Promise => { + const controller = createAbortController(); + fetchOptions.signal = controller.signal; + + let timeoutId: NodeJS.Timeout; + + // The promise will resolve with one of these two promises (the fetch or the timeout), whichever completes first. + return Promise.race([ + fetch(fetchUrl, fetchOptions), + new Promise((_, reject) => { + timeoutId = setTimeout(() => { + controller.abort(); + reject(new Error("Timeout when executing 'fetch'")); + }, timeout); + }) + ]).finally(() => { + clearTimeout(timeoutId); + }); +}; + +const fetchWithoutWorker = async ( + fetchUrl: string, + fetchOptions: FetchOptions, + timeout: number +) => { + const response = await fetchWithTimeout(fetchUrl, fetchOptions, timeout); + return { + ok: response.ok, + json: await response.json(), + /** + * This is not needed, but do it anyway so the object shape is the + * same as when using a Web Worker (which *does* need this, see + * src/worker/token.worker.ts). + */ + headers: fromEntries(response.headers) + }; +}; + +const fetchWithWorker = async ( + fetchUrl: string, + audience: string, + scope: string, + fetchOptions: FetchOptions, + timeout: number, + worker: Worker, + useFormData?: boolean, + useMrrt?: boolean, + skipTokenStorage?: boolean, + preserveRefreshToken?: boolean +) => { + return sendMessage( + { + type: 'refresh', + auth: { + audience, + scope + }, + timeout, + fetchUrl, + fetchOptions, + useFormData, + useMrrt, + skipTokenStorage, + preserveRefreshToken + }, + worker + ); +}; + +export const switchFetch = async ( + fetchUrl: string, + audience: string, + scope: string, + fetchOptions: FetchOptions, + worker?: Worker, + useFormData?: boolean, + timeout = DEFAULT_FETCH_TIMEOUT_MS, + useMrrt?: boolean, + skipTokenStorage?: boolean, + preserveRefreshToken?: boolean +): Promise => { + if (worker) { + return fetchWithWorker( + fetchUrl, + audience, + scope, + fetchOptions, + timeout, + worker, + useFormData, + useMrrt, + skipTokenStorage, + preserveRefreshToken + ); + } else { + return fetchWithoutWorker(fetchUrl, fetchOptions, timeout); + } +}; + +export async function getJSON( + url: string, + timeout: number | undefined, + audience: string, + scope: string, + options: FetchOptions, + worker?: Worker, + useFormData?: boolean, + useMrrt?: boolean, + dpop?: Pick, + isDpopRetry?: boolean, + skipTokenStorage?: boolean, + preserveRefreshToken?: boolean +): Promise { + if (dpop) { + const dpopProof = await dpop.generateProof({ + url, + method: options.method || 'GET', + nonce: await dpop.getNonce() + }); + + options.headers = { ...options.headers, dpop: dpopProof }; + } + + let fetchError: null | Error = null; + let response!: FetchResponse; + + for (let i = 0; i < DEFAULT_SILENT_TOKEN_RETRY_COUNT; i++) { + try { + response = await switchFetch( + url, + audience, + scope, + options, + worker, + useFormData, + timeout, + useMrrt, + skipTokenStorage, + preserveRefreshToken + ); + fetchError = null; + break; + } catch (e) { + // Fetch only fails in the case of a network issue, so should be + // retried here. Failure status (4xx, 5xx, etc) return a resolved Promise + // with the failure in the body. + // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API + fetchError = e; + } + } + + if (fetchError) { + throw fetchError; + } + + const { + json: { error, error_description, ...data }, + headers, + ok + } = response; + + let newDpopNonce: string | undefined; + + if (dpop) { + /** + * Note that a new DPoP nonce can appear in both error and success responses! + * + * @see {@link https://www.rfc-editor.org/rfc/rfc9449.html#section-8.2-3} + */ + newDpopNonce = headers[DPOP_NONCE_HEADER]; + + if (newDpopNonce) { + await dpop.setNonce(newDpopNonce); + } + } + + if (!ok) { + const errorMessage = + error_description || `HTTP error. Unable to fetch ${url}`; + + if (error === 'mfa_required') { + throw new MfaRequiredError(error, errorMessage, data.mfa_token, data.mfa_requirements); + } + + if (error === 'missing_refresh_token') { + throw new MissingRefreshTokenError(audience, scope); + } + + /** + * When DPoP is used and we get a `use_dpop_nonce` error from the server, + * we must retry ONCE with any new nonce received in the rejected request. + * + * If a new nonce was not received or the retry fails again, we give up and + * throw the error as is. + */ + if (error === 'use_dpop_nonce') { + if (!dpop || !newDpopNonce || isDpopRetry) { + throw new UseDpopNonceError(newDpopNonce); + } + + // repeat the call but with isDpopRetry=true to avoid any more retries + return getJSON( + url, + timeout, + audience, + scope, + options, + worker, + useFormData, + useMrrt, + dpop, + true, // ! + skipTokenStorage, + preserveRefreshToken + ); + } + + throw new GenericError(error || 'request_error', errorMessage); + } + + return data; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/index.ts b/backend/node_modules/@auth0/auth0-spa-js/src/index.ts new file mode 100644 index 00000000..2c76fa18 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/index.ts @@ -0,0 +1,172 @@ +import { Auth0Client } from './Auth0Client'; +import { Auth0ClientOptions } from './global'; + +import './global'; + +export * from './global'; + +/** + * Online mode requires `useRefreshTokens: true` and `useDpop: true`, enforced here at + * compile time. Dynamic values, casts, and plain JS are covered by the runtime check in + * the `Auth0Client` constructor. + */ +export async function createAuth0Client( + options: Auth0ClientOptions & { + refreshTokenMode: 'online'; + useRefreshTokens: true; + useDpop: true; + } +): Promise; +export async function createAuth0Client( + options: Auth0ClientOptions & { refreshTokenMode?: 'offline' } +): Promise; +/** + * Asynchronously creates the Auth0Client instance and calls `checkSession`. + * + * **Note:** There are caveats to using this in a private browser tab, which may not silently authenticate + * a user on page refresh. Please see [the checkSession docs](https://auth0.github.io/auth0-spa-js/classes/Auth0Client.html#checksession) for more info. + * + * @param options The client options + * @returns An instance of Auth0Client + */ +export async function createAuth0Client(options: Auth0ClientOptions) { + const auth0 = new Auth0Client(options); + await auth0.checkSession(); + return auth0; +} + +export { Auth0Client }; + +export { + ConnectError, + GenericError, + InvalidConfigurationError, + AuthenticationError, + TimeoutError, + PopupTimeoutError, + PopupCancelledError, + PopupOpenError, + MfaRequiredError, + MissingRefreshTokenError, + MissingScopesError, + UseDpopNonceError +} from './errors'; + +export type { MfaRequirements } from './errors'; + +export { + MfaError, + MfaListAuthenticatorsError, + MfaEnrollmentError, + MfaChallengeError, + MfaVerifyError, + MfaEnrollmentFactorsError +} from './mfa/errors'; + +export { MfaApiClient } from './mfa'; + +export type { + MfaFactorType, + EnrollParams, + EnrollOtpParams, + EnrollSmsParams, + EnrollVoiceParams, + EnrollEmailParams, + EnrollPushParams, + VerifyParams +} from './mfa'; + +export { + LocalStorageCache, + InMemoryCache, + CacheKey +} from './cache'; + +export type { + ICache, + Cacheable, + DecodedToken, + CacheEntry, + WrappedCacheEntry, + KeyManifestEntry, + MaybePromise, + CacheKeyData +} from './cache'; + +export type { + FetcherConfig, + Fetcher, + CustomFetchMinimalOutput +} from './fetcher'; + +export { MyAccountApiClient, MyAccountApiError } from './myaccount'; +export type { + ConnectRequest, + ConnectResponse, + CompleteRequest, + CompleteResponse, + ErrorResponse, + Factor, + AuthenticationMethodType, + AuthenticationMethod, + PasskeyAuthenticationMethod, + WebAuthnPlatformAuthenticationMethod, + WebAuthnRoamingAuthenticationMethod, + PhoneAuthenticationMethod, + EmailAuthenticationMethod, + PasswordAuthenticationMethod, + TotpAuthenticationMethod, + PushNotificationAuthenticationMethod, + RecoveryCodeAuthenticationMethod, + UpdateAuthenticationMethodRequest, + EnrollmentChallengeOptions, + PasskeyEnrollmentChallengeOptions, + PhoneEnrollmentChallengeOptions, + EmailEnrollmentChallengeOptions, + TotpEnrollmentChallengeOptions, + PushNotificationEnrollmentChallengeOptions, + RecoveryCodeEnrollmentChallengeOptions, + PasswordEnrollmentChallengeOptions, + EnrollmentChallengeResponse, + PasskeyEnrollmentChallengeResponse, + PhoneEnrollmentChallengeResponse, + EmailEnrollmentChallengeResponse, + TotpEnrollmentChallengeResponse, + PushNotificationEnrollmentChallengeResponse, + RecoveryCodeEnrollmentChallengeResponse, + PasswordEnrollmentChallengeResponse, + PasswordPolicy, + EnrollmentVerifyOptions, + PasskeyEnrollmentVerifyOptions, + PhoneEnrollmentVerifyOptions, + EmailEnrollmentVerifyOptions, + TotpEnrollmentVerifyOptions, + PushNotificationEnrollmentVerifyOptions, + RecoveryCodeEnrollmentVerifyOptions, + PasswordEnrollmentVerifyOptions +} from './myaccount'; + +export { + PasskeyApiClient, + PasskeyError, + PasskeyRegisterError, + PasskeyChallengeError, + PasskeyGetTokenError +} from './passkey'; +export type { + PasskeyErrorResponse, + PasskeySignupChallengeOptions, + PasskeySignupChallengeResponse, + PasskeyLoginChallengeOptions, + PasskeyLoginChallengeResponse, + PasskeyCredentialResponse, + PasskeyCreationOptions, + PasskeyRequestOptions, + PasskeySignupOptions, + PasskeyLoginOptions, + PasskeySignupChallenge, + PasskeyLoginChallenge, + PasskeyGetTokenOptions +} from './passkey'; + +export type { CustomTokenExchangeOptions } from './TokenExchange'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/jwt.ts b/backend/node_modules/@auth0/auth0-spa-js/src/jwt.ts new file mode 100644 index 00000000..4ffaa597 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/jwt.ts @@ -0,0 +1,225 @@ +import { urlDecodeB64 } from './utils'; +import { IdToken, JWTVerifyOptions } from './global'; + +const isNumber = (n: any) => typeof n === 'number'; + +const idTokendecoded = [ + 'iss', + 'aud', + 'exp', + 'nbf', + 'iat', + 'jti', + 'azp', + 'nonce', + 'auth_time', + 'at_hash', + 'c_hash', + 'acr', + 'amr', + 'sub_jwk', + 'cnf', + 'sip_from_tag', + 'sip_date', + 'sip_callid', + 'sip_cseq_num', + 'sip_via_branch', + 'orig', + 'dest', + 'mky', + 'events', + 'toe', + 'txn', + 'rph', + 'sid', + 'vot', + 'vtm' +]; + +export const decode = (token: string) => { + const parts = token.split('.'); + const [header, payload, signature] = parts; + + if (parts.length !== 3 || !header || !payload || !signature) { + throw new Error('ID token could not be decoded'); + } + const payloadJSON = JSON.parse(urlDecodeB64(payload)); + const claims: IdToken = { __raw: token }; + const user: any = {}; + Object.keys(payloadJSON).forEach(k => { + claims[k] = payloadJSON[k]; + if (!idTokendecoded.includes(k)) { + user[k] = payloadJSON[k]; + } + }); + return { + encoded: { header, payload, signature }, + header: JSON.parse(urlDecodeB64(header)), + claims, + user + }; +}; + +export const verify = (options: JWTVerifyOptions) => { + if (!options.id_token) { + throw new Error('ID token is required but missing'); + } + + const decoded = decode(options.id_token); + + if (!decoded.claims.iss) { + throw new Error( + 'Issuer (iss) claim must be a string present in the ID token' + ); + } + + if (decoded.claims.iss !== options.iss) { + throw new Error( + `Issuer (iss) claim mismatch in the ID token; expected "${options.iss}", found "${decoded.claims.iss}"` + ); + } + + if (!decoded.user.sub) { + throw new Error( + 'Subject (sub) claim must be a string present in the ID token' + ); + } + + if (decoded.header.alg !== 'RS256') { + throw new Error( + `Signature algorithm of "${decoded.header.alg}" is not supported. Expected the ID token to be signed with "RS256".` + ); + } + + if ( + !decoded.claims.aud || + !( + typeof decoded.claims.aud === 'string' || + Array.isArray(decoded.claims.aud) + ) + ) { + throw new Error( + 'Audience (aud) claim must be a string or array of strings present in the ID token' + ); + } + if (Array.isArray(decoded.claims.aud)) { + if (!decoded.claims.aud.includes(options.aud)) { + throw new Error( + `Audience (aud) claim mismatch in the ID token; expected "${ + options.aud + }" but was not one of "${decoded.claims.aud.join(', ')}"` + ); + } + if (decoded.claims.aud.length > 1) { + if (!decoded.claims.azp) { + throw new Error( + 'Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values' + ); + } + if (decoded.claims.azp !== options.aud) { + throw new Error( + `Authorized Party (azp) claim mismatch in the ID token; expected "${options.aud}", found "${decoded.claims.azp}"` + ); + } + } + } else if (decoded.claims.aud !== options.aud) { + throw new Error( + `Audience (aud) claim mismatch in the ID token; expected "${options.aud}" but found "${decoded.claims.aud}"` + ); + } + if (options.nonce) { + if (!decoded.claims.nonce) { + throw new Error( + 'Nonce (nonce) claim must be a string present in the ID token' + ); + } + if (decoded.claims.nonce !== options.nonce) { + throw new Error( + `Nonce (nonce) claim mismatch in the ID token; expected "${options.nonce}", found "${decoded.claims.nonce}"` + ); + } + } + + if (options.max_age && !isNumber(decoded.claims.auth_time)) { + throw new Error( + 'Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified' + ); + } + + /* c8 ignore next 5 */ + if (decoded.claims.exp == null || !isNumber(decoded.claims.exp)) { + throw new Error( + 'Expiration Time (exp) claim must be a number present in the ID token' + ); + } + if (!isNumber(decoded.claims.iat)) { + throw new Error( + 'Issued At (iat) claim must be a number present in the ID token' + ); + } + + const leeway = options.leeway || 60; + const now = new Date(options.now || Date.now()); + const expDate = new Date(0); + + expDate.setUTCSeconds(decoded.claims.exp + leeway); + + if (now > expDate) { + throw new Error( + `Expiration Time (exp) claim error in the ID token; current time (${now}) is after expiration time (${expDate})` + ); + } + + if (decoded.claims.nbf != null && isNumber(decoded.claims.nbf)) { + const nbfDate = new Date(0); + nbfDate.setUTCSeconds(decoded.claims.nbf - leeway); + if (now < nbfDate) { + throw new Error( + `Not Before time (nbf) claim in the ID token indicates that this token can't be used just yet. Current time (${now}) is before ${nbfDate}` + ); + } + } + + if (decoded.claims.auth_time != null && isNumber(decoded.claims.auth_time)) { + const authTimeDate = new Date(0); + authTimeDate.setUTCSeconds( + parseInt(decoded.claims.auth_time) + (options.max_age as number) + leeway + ); + + if (now > authTimeDate) { + throw new Error( + `Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (${now}) is after last auth at ${authTimeDate}` + ); + } + } + + if (options.organization) { + const org = options.organization.trim(); + if (org.startsWith('org_')) { + const orgId = org; + if (!decoded.claims.org_id) { + throw new Error( + 'Organization ID (org_id) claim must be a string present in the ID token' + ); + } else if (orgId !== decoded.claims.org_id) { + throw new Error( + `Organization ID (org_id) claim mismatch in the ID token; expected "${orgId}", found "${decoded.claims.org_id}"` + ); + } + } else { + const orgName = org.toLowerCase(); + // TODO should we verify if there is an `org_id` claim? + if (!decoded.claims.org_name) { + throw new Error( + 'Organization Name (org_name) claim must be a string present in the ID token' + ); + } else if (orgName !== decoded.claims.org_name) { + throw new Error( + `Organization Name (org_name) claim mismatch in the ID token; expected "${orgName}", found "${decoded.claims.org_name}"` + ); + } + } + } + + return decoded; +}; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/lock.ts b/backend/node_modules/@auth0/auth0-spa-js/src/lock.ts new file mode 100644 index 00000000..a880281f --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/lock.ts @@ -0,0 +1,141 @@ +import BrowserTabsLock from 'browser-tabs-lock'; +import { TimeoutError } from './errors'; + +/** + * Lock manager abstraction for cross-tab synchronization. + * Supports both modern Web Locks API and legacy localStorage-based locking. + */ + +/** Lock manager interface - callback pattern ensures automatic lock release */ +export interface ILockManager { + /** + * Run callback while holding a lock. + * Lock is automatically released when callback completes or throws. + * + * @param key - Lock identifier + * @param timeout - Maximum time to wait for lock acquisition (ms) + * @param callback - Function to execute while holding the lock + * @returns Promise resolving to callback's return value + * @throws Error if lock cannot be acquired within timeout + */ + runWithLock( + key: string, + timeout: number, + callback: () => Promise + ): Promise; +} + +/** Web Locks API implementation - true mutex with OS-level queuing */ +export class WebLocksApiManager implements ILockManager { + async runWithLock( + key: string, + timeout: number, + callback: () => Promise + ): Promise { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + return await navigator.locks.request( + key, + { mode: 'exclusive', signal: controller.signal }, + async lock => { + clearTimeout(timeoutId); + if (!lock) throw new Error('Lock not available'); + return await callback(); + } + ); + } catch (error: any) { + clearTimeout(timeoutId); + if (error?.name === 'AbortError') throw new TimeoutError(); + throw error; + } + } +} + +/** Legacy localStorage-based locking with retry logic for older browsers */ +export class LegacyLockManager implements ILockManager { + private lock: BrowserTabsLock; + private activeLocks: Set = new Set(); + private pagehideHandler: () => void; + + constructor() { + this.lock = new BrowserTabsLock(); + + this.pagehideHandler = () => { + this.activeLocks.forEach(key => this.lock.releaseLock(key)); + this.activeLocks.clear(); + }; + } + + async runWithLock( + key: string, + timeout: number, + callback: () => Promise + ): Promise { + // Retry logic to handle race conditions in localStorage-based locking + const retryAttempts = 10; + let acquired = false; + + for (let i = 0; i < retryAttempts && !acquired; i++) { + acquired = await this.lock.acquireLock(key, timeout); + } + + if (!acquired) { + throw new TimeoutError(); + } + + this.activeLocks.add(key); + + // Add pagehide listener when acquiring first lock + if (this.activeLocks.size === 1 && typeof window !== 'undefined') { + window.addEventListener('pagehide', this.pagehideHandler); + } + + try { + return await callback(); + } finally { + this.activeLocks.delete(key); + await this.lock.releaseLock(key); + + // Remove pagehide listener when all locks are released + if (this.activeLocks.size === 0 && typeof window !== 'undefined') { + window.removeEventListener('pagehide', this.pagehideHandler); + } + } + } +} + +/** + * Feature detection for Web Locks API support + */ +function isWebLocksSupported(): boolean { + return ( + typeof navigator !== 'undefined' && + typeof navigator.locks?.request === 'function' + ); +} + +function createLockManager(): ILockManager { + return isWebLocksSupported() + ? new WebLocksApiManager() + : new LegacyLockManager(); +} + +/** + * Get the singleton lock manager instance. + * Uses Web Locks API in modern browsers, falls back to localStorage in older browsers. + */ +let lockManager: ILockManager | null = null; + +export function getLockManager(): ILockManager { + if (!lockManager) { + lockManager = createLockManager(); + } + return lockManager; +} + +// For testing: allow resetting the singleton +export function resetLockManager(): void { + lockManager = null; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaApiClient.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaApiClient.ts new file mode 100644 index 00000000..3a646641 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaApiClient.ts @@ -0,0 +1,422 @@ +import { Auth0Client } from '../Auth0Client'; +import type { TokenEndpointResponse } from '../global'; +import type { + Authenticator, + EnrollParams, + EnrollmentResponse, + ChallengeAuthenticatorParams, + ChallengeResponse, + VerifyParams, + OobChannel, + ChallengeType, + EnrollmentFactor +} from './types'; +import { getAuthJsEnrollParams, getGrantType } from './utils'; +import { + MfaClient as Auth0AuthJsMfaClient, + MfaListAuthenticatorsError as Auth0JsMfaListAuthenticatorsError, + MfaEnrollmentError as Auth0JsMfaEnrollmentError, + MfaChallengeError as Auth0JsMfaChallengeError +} from '@auth0/auth0-auth-js'; +import { + MfaListAuthenticatorsError, + MfaEnrollmentError, + MfaChallengeError, + MfaVerifyError, + MfaEnrollmentFactorsError +} from './errors'; +import { MfaRequirements } from '../errors'; +import { MfaContextManager } from './MfaContextManager'; + +/** + * Client for Auth0 MFA API operations + * + * Manages multi-factor authentication including: + * - Listing enrolled authenticators + * - Enrolling new authenticators (OTP, SMS, Voice, Push, Email) + * - Initiating MFA challenges + * - Verifying MFA challenges + * + * This is a wrapper around auth0-auth-js MfaClient that maintains + * backward compatibility with the existing spa-js API. + * + * MFA context (scope, audience) is stored internally keyed by mfaToken, + * enabling concurrent MFA flows without state conflicts. + * + * @example + * ```typescript + * try { + * await auth0.getTokenSilently({ authorizationParams: { audience: 'https://api.example.com' } }); + * } catch (e) { + * if (e instanceof MfaRequiredError) { + * // SDK automatically stores context for this mfaToken + * const authenticators = await auth0.mfa.getAuthenticators({ mfaToken: e.mfa_token }); + * // ... complete MFA flow + * } + * } + * ``` + */ +export class MfaApiClient { + private authJsMfaClient: Auth0AuthJsMfaClient; + private auth0Client: Auth0Client; + private contextManager: MfaContextManager; + + /** + * @internal + * Do not instantiate directly. Use Auth0Client.mfa instead. + */ + constructor(authJsMfaClient: Auth0AuthJsMfaClient, auth0Client: Auth0Client) { + this.authJsMfaClient = authJsMfaClient; + this.auth0Client = auth0Client; + this.contextManager = new MfaContextManager(); + } + + /** + * @internal + * Stores authentication details (scope, audience, and MFA requirements) for MFA token verification. + * This is automatically called by Auth0Client when an mfa_required error occurs. + * + * The context is stored keyed by the MFA token, enabling concurrent MFA flows. + * + * @param mfaToken - The MFA token from the mfa_required error response + * @param scope - The OAuth scope from the original request (optional) + * @param audience - The API audience from the original request (optional) + * @param mfaRequirements - The MFA requirements from the mfa_required error (optional) + */ + public setMFAAuthDetails( + mfaToken: string, + scope?: string, + audience?: string, + mfaRequirements?: MfaRequirements + ) { + this.contextManager.set(mfaToken, { scope, audience, mfaRequirements }); + } + + /** + * Gets enrolled MFA authenticators filtered by challenge types from context. + * + * Challenge types are automatically resolved from the stored MFA context + * (set when mfa_required error occurred). + * + * @param mfaToken - MFA token from mfa_required error + * @returns Array of enrolled authenticators matching the challenge types + * @throws {MfaListAuthenticatorsError} If the request fails or context not found + * + * @example Basic usage + * ```typescript + * try { + * await auth0.getTokenSilently(); + * } catch (e) { + * if (e instanceof MfaRequiredError) { + * // SDK automatically uses challenge types from error context + * const authenticators = await auth0.mfa.getAuthenticators(e.mfa_token); + * } + * } + * ``` + */ + public async getAuthenticators(mfaToken: string): Promise { + const context = this.contextManager.get(mfaToken); + + if (!context) { + throw new MfaListAuthenticatorsError( + 'invalid_request', + 'MFA context not found for this MFA token' + ); + } + + // step-up policy: challenge types are present, filter to only accepted types + // global MFA policy: challenge types are absent, all authenticators are valid + const challengeTypes = context.mfaRequirements?.challenge?.map( + c => c.type + ) as ChallengeType[] | undefined; + + try { + const allAuthenticators = await this.authJsMfaClient.listAuthenticators({ + mfaToken + }); + + if (!challengeTypes || challengeTypes.length === 0) { + return allAuthenticators; + } + + return allAuthenticators.filter(auth => { + if (!auth.type) return false; + return challengeTypes.includes(auth.type as ChallengeType); + }); + } catch (error: unknown) { + if (error instanceof Auth0JsMfaListAuthenticatorsError) { + throw new MfaListAuthenticatorsError( + error.cause?.error!, + error.message + ); + } + throw error; + } + } + + /** + * Enrolls a new MFA authenticator + * + * Requires MFA access token with 'enroll' scope + * + * @param params - Enrollment parameters including mfaToken and factorType + * @returns Enrollment response with authenticator details + * @throws {MfaEnrollmentError} If enrollment fails + * + * @example OTP enrollment + * ```typescript + * const enrollment = await mfa.enroll({ + * mfaToken: mfaToken, + * factorType: 'otp' + * }); + * console.log(enrollment.secret); // Base32 secret + * console.log(enrollment.barcodeUri); // QR code URI + * ``` + * + * @example SMS enrollment + * ```typescript + * const enrollment = await mfa.enroll({ + * mfaToken: mfaToken, + * factorType: 'sms', + * phoneNumber: '+12025551234' + * }); + * ``` + */ + public async enroll( + params: EnrollParams + ): Promise { + const authJsParams = getAuthJsEnrollParams(params); + + try { + // Type assertion is safe here because getAuthJsEnrollParams ensures correct mapping + return await this.authJsMfaClient.enrollAuthenticator(authJsParams as any); + } catch (error: unknown) { + if (error instanceof Auth0JsMfaEnrollmentError) { + throw new MfaEnrollmentError( + error.cause?.error!, + error.message + ); + } + throw error; + } + } + + /** + * Initiates an MFA challenge + * + * Sends OTP via SMS, initiates push notification, or prepares for OTP entry + * + * @param params - Challenge parameters including mfaToken + * @returns Challenge response with oobCode if applicable + * @throws {MfaChallengeError} If challenge initiation fails + * + * @example OTP challenge + * ```typescript + * const challenge = await mfa.challenge({ + * mfaToken: mfaTokenFromLogin, + * challengeType: 'otp', + * authenticatorId: 'otp|dev_xxx' + * }); + * // User enters OTP from their authenticator app + * ``` + * + * @example SMS challenge + * ```typescript + * const challenge = await mfa.challenge({ + * mfaToken: mfaTokenFromLogin, + * challengeType: 'oob', + * authenticatorId: 'sms|dev_xxx' + * }); + * console.log(challenge.oobCode); // Use for verification + * ``` + */ + public async challenge( + params: ChallengeAuthenticatorParams + ): Promise { + try { + const authJsParams: { + challengeType: 'otp' | 'oob'; + authenticatorId?: string; + oobChannel?: OobChannel; + mfaToken: string; + } = { + challengeType: params.challengeType, + mfaToken: params.mfaToken + }; + + if (params.authenticatorId) { + authJsParams.authenticatorId = params.authenticatorId; + } + + return await this.authJsMfaClient.challengeAuthenticator(authJsParams); + } catch (error: unknown) { + if (error instanceof Auth0JsMfaChallengeError) { + throw new MfaChallengeError( + error.cause?.error!, + error.message + ); + } + throw error; + } + } + + /** + * Gets available MFA enrollment factors from the stored context. + * + * This method exposes the enrollment options from the mfa_required error's + * mfaRequirements.enroll array, eliminating the need for manual parsing. + * + * @param mfaToken - MFA token from mfa_required error + * @returns Array of enrollment factors available for the user (empty array if no enrollment required) + * @throws {MfaEnrollmentFactorsError} If MFA context not found + * + * @example Basic usage + * ```typescript + * try { + * await auth0.getTokenSilently(); + * } catch (error) { + * if (error.error === 'mfa_required') { + * // Get enrollment options from SDK + * const enrollOptions = await auth0.mfa.getEnrollmentFactors(error.mfa_token); + * // [{ type: 'otp' }, { type: 'phone' }, { type: 'push-notification' }] + * + * showEnrollmentOptions(enrollOptions); + * } + * } + * ``` + * + * @example Check if enrollment is required + * ```typescript + * try { + * const factors = await auth0.mfa.getEnrollmentFactors(mfaToken); + * if (factors.length > 0) { + * // User needs to enroll in MFA + * renderEnrollmentUI(factors); + * } else { + * // No enrollment required, proceed with challenge + * } + * } catch (error) { + * if (error instanceof MfaEnrollmentFactorsError) { + * console.error('Context not found:', error.error_description); + * } + * } + * ``` + */ + public async getEnrollmentFactors( + mfaToken: string + ): Promise { + const context = this.contextManager.get(mfaToken); + + if (!context || !context.mfaRequirements) { + throw new MfaEnrollmentFactorsError( + 'mfa_context_not_found', + 'MFA context not found for this MFA token. Please retry the original request to get a new MFA token.' + ); + } + + // Return empty array if no enrollment required (not an error case) + if (!context.mfaRequirements.enroll || context.mfaRequirements.enroll.length === 0) { + return []; + } + + return context.mfaRequirements.enroll; + } + + /** + * Verifies an MFA challenge and completes authentication + * + * The scope and audience are retrieved from the stored context (set when the + * mfa_required error occurred). The grant_type is automatically inferred from + * which verification field is provided (otp, oobCode, or recoveryCode). + * + * @param params - Verification parameters with OTP, OOB code, or recovery code + * @returns Token response with access_token, id_token, refresh_token + * @throws {MfaVerifyError} If verification fails (invalid code, expired, rate limited) + * @throws {MfaVerifyError} If MFA context not found + * @throws {MfaVerifyError} If grant_type cannot be inferred + * + * Rate limits: + * - 10 verification attempts allowed + * - Refreshes at 1 attempt per 6 minutes + * + * @example OTP verification (grant_type inferred from otp field) + * ```typescript + * const tokens = await mfa.verify({ + * mfaToken: mfaTokenFromLogin, + * otp: '123456' + * }); + * console.log(tokens.access_token); + * ``` + * + * @example OOB verification (grant_type inferred from oobCode field) + * ```typescript + * const tokens = await mfa.verify({ + * mfaToken: mfaTokenFromLogin, + * oobCode: challenge.oobCode, + * bindingCode: '123456' // Code user received via SMS + * }); + * ``` + * + * @example Recovery code verification (grant_type inferred from recoveryCode field) + * ```typescript + * const tokens = await mfa.verify({ + * mfaToken: mfaTokenFromLogin, + * recoveryCode: 'XXXX-XXXX-XXXX' + * }); + * ``` + */ + public async verify( + params: VerifyParams + ): Promise { + // Look up stored context for this MFA token + const context = this.contextManager.get(params.mfaToken); + + // Use context values only (set when mfa_required error occurred) + if (!context) { + throw new MfaVerifyError( + 'mfa_context_not_found', + 'MFA context not found for this MFA token. Please retry the original request to get a new MFA token.' + ); + } + + // Get grant type from verification fields + const grantType = getGrantType(params); + + if (!grantType) { + throw new MfaVerifyError( + 'invalid_request', + 'Unable to determine grant type. Provide one of: otp, oobCode, or recoveryCode.' + ); + } + + const scope = context.scope; + const audience = context.audience; + + try { + const result = await this.auth0Client._requestTokenForMfa({ + grant_type: grantType, + mfaToken: params.mfaToken, + scope, + audience, + otp: params.otp, + oob_code: params.oobCode, + binding_code: params.bindingCode, + recovery_code: params.recoveryCode + }); + + // Clean up context after successful verification + this.contextManager.remove(params.mfaToken); + + return result; + } catch (error: unknown) { + if (error instanceof MfaVerifyError) { + throw new MfaVerifyError( + error.error, + error.error_description + ); + } + // MfaRequiredError is handled upstream in Auth0Client._requestToken before reaching here. + throw error; + } + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaContextManager.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaContextManager.ts new file mode 100644 index 00000000..4068e144 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/MfaContextManager.ts @@ -0,0 +1,128 @@ +import { MfaRequirements } from '../errors'; + +/** + * Represents the stored context for an MFA flow + */ +export interface MfaContext { + /** The OAuth scope for the original token request */ + scope?: string; + /** The API audience for the original token request */ + audience?: string; + /** MFA requirements from the mfa_required error (camelCase for TypeScript conventions) */ + mfaRequirements?: MfaRequirements; + /** Timestamp when the context was created */ + createdAt: number; +} + +/** + * Default TTL for MFA contexts in milliseconds (10 minutes) + * This aligns with typical MFA token expiration times + */ +const DEFAULT_TTL_MS = 10 * 60 * 1000; + +/** + * Manages MFA authentication contexts keyed by MFA token. + * + * When an mfa_required error occurs, the SDK stores the original request's + * scope and audience. When the user later provides an MFA token for verification, + * the SDK retrieves the matching context to complete the token exchange. + * + * This enables concurrent MFA flows without state conflicts. + * + * @example + * ```typescript + * const manager = new MfaContextManager(); + * + * // Store context when mfa_required error occurs + * manager.set('mfaTokenAbc', { scope: 'openid profile', audience: 'https://api.example.com' }); + * + * // Retrieve context when user completes MFA + * const context = manager.get('mfaTokenAbc'); + * // { scope: 'openid profile', audience: 'https://api.example.com', createdAt: ... } + * + * // Remove after successful verification + * manager.remove('mfaTokenAbc'); + * ``` + */ +export class MfaContextManager { + private contexts: Map = new Map(); + private readonly ttlMs: number; + + /** + * Creates a new MfaContextManager + * @param ttlMs - Time-to-live for contexts in milliseconds (default: 10 minutes) + */ + constructor(ttlMs: number = DEFAULT_TTL_MS) { + this.ttlMs = ttlMs; + } + + /** + * Stores an MFA context keyed by the MFA token. + * Runs cleanup to remove expired entries before storing. + * + * @param mfaToken - The MFA token from the mfa_required error + * @param context - The scope and audience from the original request + */ + public set( + mfaToken: string, + context: Omit + ): void { + this.cleanup(); + this.contexts.set(mfaToken, { + ...context, + createdAt: Date.now() + }); + } + + /** + * Retrieves the MFA context for a given token. + * Returns undefined if the token is not found or has expired. + * + * @param mfaToken - The MFA token to look up + * @returns The stored context, or undefined if not found/expired + */ + public get(mfaToken: string): MfaContext | undefined { + const context = this.contexts.get(mfaToken); + if (!context) { + return undefined; + } + + // Check if expired + if (Date.now() - context.createdAt > this.ttlMs) { + this.contexts.delete(mfaToken); + return undefined; + } + + return context; + } + + /** + * Removes an MFA context. + * Should be called after successful MFA verification. + * + * @param mfaToken - The MFA token to remove + */ + public remove(mfaToken: string): void { + this.contexts.delete(mfaToken); + } + + /** + * Removes all expired contexts from the Map. + * Called automatically on every `set` operation. + */ + private cleanup(): void { + const now = Date.now(); + for (const [key, value] of this.contexts) { + if (now - value.createdAt > this.ttlMs) { + this.contexts.delete(key); + } + } + } + + /** + * Returns the number of stored contexts + */ + public get size(): number { + return this.contexts.size; + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/constants.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/constants.ts new file mode 100644 index 00000000..6a6b4517 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/constants.ts @@ -0,0 +1,48 @@ +import type { MfaFactorType, OobChannel } from './types'; + +/** + * Mapping configuration for a factor type + */ +export interface FactorMapping { + authenticatorTypes: ['otp'] | ['oob']; + oobChannels?: OobChannel[]; +} + +/** + * Maps MFA factor types to auth-js enrollment parameters + */ +export const FACTOR_MAPPING: Record = { + otp: { + authenticatorTypes: ['otp'] + }, + sms: { + authenticatorTypes: ['oob'], + oobChannels: ['sms'] + }, + email: { + authenticatorTypes: ['oob'], + oobChannels: ['email'] + }, + push: { + authenticatorTypes: ['oob'], + oobChannels: ['auth0'] + }, + voice: { + authenticatorTypes: ['oob'], + oobChannels: ['voice'] + } +}; + +/** + * MFA grant type constants for verification + */ +export const MfaGrantTypes = { + /** Grant type for OTP (TOTP) verification */ + OTP: 'http://auth0.com/oauth/grant-type/mfa-otp', + + /** Grant type for OOB (SMS, Email, Push) verification */ + OOB: 'http://auth0.com/oauth/grant-type/mfa-oob', + + /** Grant type for recovery code verification */ + RECOVERY_CODE: 'http://auth0.com/oauth/grant-type/mfa-recovery-code' +} as const; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/errors.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/errors.ts new file mode 100644 index 00000000..b9e1a4ce --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/errors.ts @@ -0,0 +1,154 @@ +import { MfaApiErrorResponse } from '@auth0/auth0-auth-js'; +import { GenericError } from '../errors'; + +/** + * Base class for MFA-related errors in auth0-spa-js. + * Extends GenericError for unified error hierarchy across the SDK. + */ +export class MfaError extends GenericError { + constructor(error: string, error_description: string) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaError.prototype); + } + + static fromPayload({ + error, + error_description + }: { + error: string; + error_description: string; + }) { + return new MfaError(error, error_description); + } +} + +/** + * Error thrown when listing MFA authenticators fails. + * + * @example + * ```typescript + * try { + * const authenticators = await mfa.getAuthenticators(); + * } catch (error) { + * if (error instanceof MfaListAuthenticatorsError) { + * console.log(error.error); // 'access_denied' + * console.log(error.error_description); // 'Unauthorized' + * } + * } + * ``` + */ +export class MfaListAuthenticatorsError extends MfaError { + constructor(error: string, error_description: string) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaListAuthenticatorsError.prototype); + } +} + +/** + * Error thrown when enrolling an MFA authenticator fails. + * + * @example + * ```typescript + * try { + * const enrollment = await mfa.enroll({ + * authenticator_types: ['otp'] + * }); + * } catch (error) { + * if (error instanceof MfaEnrollmentError) { + * console.log(error.error); // 'invalid_phone_number' + * console.log(error.error_description); // 'Invalid phone number format' + * } + * } + * ``` + */ +export class MfaEnrollmentError extends MfaError { + constructor(error: string, error_description: string) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaEnrollmentError.prototype); + } +} + +/** + * Error thrown when initiating an MFA challenge fails. + * + * @example + * ```typescript + * try { + * const challenge = await mfa.challenge({ + * mfaToken: mfaToken, + * challengeType: 'otp', + * authenticatorId: 'otp|dev_123' + * }); + * } catch (error) { + * if (error instanceof MfaChallengeError) { + * console.log(error.error); // 'too_many_attempts' + * console.log(error.error_description); // 'Rate limit exceeded' + * } + * } + * ``` + */ +export class MfaChallengeError extends MfaError { + constructor(error: string, error_description: string) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaChallengeError.prototype); + } +} + +/** + * Error thrown when verifying an MFA challenge fails. + * + * @example + * ```typescript + * try { + * const tokens = await mfa.verify({ + * mfaToken: mfaToken, + * grant_type: 'http://auth0.com/oauth/grant-type/mfa-otp', + * otp: '123456' + * }); + * } catch (error) { + * if (error instanceof MfaVerifyError) { + * console.log(error.error); // 'invalid_otp' or 'context_not_found' + * console.log(error.error_description); // Error details + * } + * } + * ``` + */ +export class MfaVerifyError extends MfaError { + constructor(error: string, error_description: string) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaVerifyError.prototype); + } +} + +/** + * Error thrown when getting enrollment factors fails. + * + * @example + * ```typescript + * try { + * const factors = await mfa.getEnrollmentFactors(mfaToken); + * } catch (error) { + * if (error instanceof MfaEnrollmentFactorsError) { + * console.log(error.error); // 'mfa_context_not_found' + * console.log(error.error_description); // 'MFA context not found...' + * } + * } + * ``` + */ +export class MfaEnrollmentFactorsError extends MfaError { + constructor(error: string, error_description: string) { + super(error, error_description); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, MfaEnrollmentFactorsError.prototype); + } +} + +/** + * Re-export MfaApiErrorResponse type for convenience + */ +export type { MfaApiErrorResponse }; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/index.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/index.ts new file mode 100644 index 00000000..cdb742ed --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/index.ts @@ -0,0 +1,24 @@ +export { MfaApiClient } from './MfaApiClient'; +export { MfaContextManager } from './MfaContextManager'; +export type { MfaContext } from './MfaContextManager'; +export type { + Authenticator, + AuthenticatorType, + OobChannel, + MfaFactorType, + EnrollBaseParams, + EnrollParams, + EnrollOtpParams, + EnrollSmsParams, + EnrollVoiceParams, + EnrollEmailParams, + EnrollPushParams, + EnrollmentResponse, + OtpEnrollmentResponse, + OobEnrollmentResponse, + ChallengeAuthenticatorParams, + ChallengeResponse, + VerifyParams, + MfaGrantType, + EnrollmentFactor +} from './types'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/types.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/types.ts new file mode 100644 index 00000000..ec2129eb --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/types.ts @@ -0,0 +1,209 @@ +import { MfaGrantTypes } from './constants'; + +/** + * Represents an MFA authenticator enrolled by a user + */ +export interface Authenticator { + /** Unique identifier for the authenticator */ + id: string; + /** Type of authenticator */ + authenticatorType: AuthenticatorType; + /** Whether the authenticator is active */ + active: boolean; + /** Optional friendly name */ + name?: string; + /** ISO 8601 timestamp when created */ + createdAt?: string; + /** ISO 8601 timestamp of last authentication */ + lastAuth?: string; + /** Types of MFA challenges*/ + type?: string; +} + +/** + * Supported authenticator types. + * Note: Email authenticators use 'oob' type with oobChannel: 'email' + */ +export type AuthenticatorType = 'otp' | 'oob' | 'recovery-code'; + +/** + * Types of MFA challenges + */ +export type ChallengeType = 'otp' | 'phone' | 'recovery-code' | 'email' | 'push-notification' | 'totp'; + +/** + * Out-of-band delivery channels. + * Includes 'email' which is also delivered out-of-band. + */ +export type OobChannel = 'sms' | 'voice' | 'auth0' | 'email'; + +/** + * Supported MFA factors for enrollment + */ +export type MfaFactorType = 'otp' | 'sms' | 'email' | 'push' | 'voice'; + +/** + * Base parameters for all enrollment types + */ +export interface EnrollBaseParams { + /** MFA token from mfa_required error */ + mfaToken: string; +} + +/** + * OTP (Time-based One-Time Password) enrollment parameters + */ +export interface EnrollOtpParams extends EnrollBaseParams { + /** The factor type for enrollment */ + factorType: 'otp'; +} + +/** + * SMS enrollment parameters + */ +export interface EnrollSmsParams extends EnrollBaseParams { + /** The factor type for enrollment */ + factorType: 'sms'; + /** Phone number in E.164 format (required for SMS) */ + phoneNumber: string; +} + +/** + * Voice enrollment parameters + */ +export interface EnrollVoiceParams extends EnrollBaseParams { + /** The factor type for enrollment */ + factorType: 'voice'; + /** Phone number in E.164 format (required for voice) */ + phoneNumber: string; +} + +/** + * Email enrollment parameters + */ +export interface EnrollEmailParams extends EnrollBaseParams { + /** The factor type for enrollment */ + factorType: 'email'; + /** Email address (optional, uses user's email if not provided) */ + email?: string; +} + +/** + * Push notification enrollment parameters + */ +export interface EnrollPushParams extends EnrollBaseParams { + /** The factor type for enrollment */ + factorType: 'push'; +} + +/** + * Union type for all enrollment parameter types + */ +export type EnrollParams = + | EnrollOtpParams + | EnrollSmsParams + | EnrollVoiceParams + | EnrollEmailParams + | EnrollPushParams; + +/** + * Response when enrolling an OTP authenticator + */ +export interface OtpEnrollmentResponse { + /** Authenticator type */ + authenticatorType: 'otp'; + /** Base32-encoded secret for TOTP generation */ + secret: string; + /** URI for generating QR code (otpauth://...) */ + barcodeUri: string; + /** Recovery codes for account recovery */ + recoveryCodes?: string[]; + /** Authenticator ID */ + id?: string; +} + +/** + * Response when enrolling an OOB authenticator + */ +export interface OobEnrollmentResponse { + /** Authenticator type */ + authenticatorType: 'oob'; + /** Delivery channel used */ + oobChannel: OobChannel; + /** Out-of-band code for verification */ + oobCode?: string; + /** Binding method (e.g., 'prompt' for user code entry) */ + bindingMethod?: string; + /** Recovery codes (generated when enrolling first MFA factor) */ + recoveryCodes?: string[]; + /** Authenticator ID */ + id?: string; + /** URI for QR code (for Push/Guardian enrollment) */ + barcodeUri?: string; +} + + +/** + * Union type for all enrollment response types + */ +export type EnrollmentResponse = + | OtpEnrollmentResponse + | OobEnrollmentResponse + +/** + * Parameters for initiating an MFA challenge + */ +export interface ChallengeAuthenticatorParams { + /** MFA token from mfa_required error or MFA-scoped access token */ + mfaToken: string; + /** Type of challenge to initiate */ + challengeType: 'otp' | 'oob'; + /** Specific authenticator to challenge (optional) */ + authenticatorId?: string; +} + +/** + * Response from initiating an MFA challenge + */ +export interface ChallengeResponse { + /** Type of challenge created */ + challengeType: 'otp' | 'oob'; + /** Out-of-band code (for OOB challenges) */ + oobCode?: string; + /** Binding method for OOB (e.g., 'prompt') */ + bindingMethod?: string; +} + +/** + * Grant types for MFA verification (derived from MfaGrantTypes constants) + */ +export type MfaGrantType = (typeof MfaGrantTypes)[keyof typeof MfaGrantTypes]; + +/** + * Parameters for verifying an MFA challenge. + * + * The grant_type is automatically inferred from which verification field is provided: + * - `otp` field → MFA-OTP grant type + * - `oobCode` field → MFA-OOB grant type + * - `recoveryCode` field → MFA-RECOVERY-CODE grant type + */ +export interface VerifyParams { + /** MFA token from challenge flow */ + mfaToken: string; + /** One-time password (for OTP challenges) */ + otp?: string; + /** Out-of-band code (for OOB challenges) */ + oobCode?: string; + /** Binding code (for OOB challenges with binding) */ + bindingCode?: string; + /** Recovery code (for recovery code verification) */ + recoveryCode?: string; +} + +/** + * Enrollment factor returned by getEnrollmentFactors + */ +export interface EnrollmentFactor { + /** Type of enrollment factor available */ + type: string; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/mfa/utils.ts b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/utils.ts new file mode 100644 index 00000000..3167f8ea --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/mfa/utils.ts @@ -0,0 +1,41 @@ +import { FACTOR_MAPPING, MfaGrantTypes } from './constants'; +import type { EnrollParams, VerifyParams, MfaGrantType } from './types'; + +/** + * Converts factor-based enrollment params to auth-js format + * + * @param params - The enrollment parameters with factorType + * @returns Parameters in auth-js format with authenticatorTypes/oobChannels + */ +export function getAuthJsEnrollParams(params: EnrollParams) { + const mapping = FACTOR_MAPPING[params.factorType]; + + return { + mfaToken: params.mfaToken, + authenticatorTypes: mapping.authenticatorTypes, + ...(mapping.oobChannels && { oobChannels: mapping.oobChannels }), + ...('phoneNumber' in params && { phoneNumber: params.phoneNumber }), + ...('email' in params && { email: params.email }) + }; +} + +/** + * Gets the grant type from verification parameters based on which field is provided. + * + * Priority order: otp > oobCode > recoveryCode + * + * @param params - The verification parameters + * @returns The grant type or undefined if no verification field is present + */ +export function getGrantType(params: VerifyParams): MfaGrantType | undefined { + if ('otp' in params && params.otp) { + return MfaGrantTypes.OTP; + } + if ('oobCode' in params && params.oobCode) { + return MfaGrantTypes.OOB; + } + if ('recoveryCode' in params && params.recoveryCode) { + return MfaGrantTypes.RECOVERY_CODE; + } + return undefined; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/MyAccountApiClient.ts b/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/MyAccountApiClient.ts new file mode 100644 index 00000000..8961a8bb --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/MyAccountApiClient.ts @@ -0,0 +1,283 @@ +import { Fetcher } from '../fetcher'; +import type { + ConnectRequest, + ConnectResponse, + CompleteRequest, + CompleteResponse, + ErrorResponse, + Factor, + AuthenticationMethod, + AuthenticationMethodType, + UpdateAuthenticationMethodRequest, + EnrollmentChallengeOptions, + EnrollmentChallengeResponse, + EnrollmentVerifyOptions +} from './types'; + +export type { + ConnectRequest, + ConnectResponse, + CompleteRequest, + CompleteResponse, + ErrorResponse, + Factor, + AuthenticationMethod, + AuthenticationMethodType, + UpdateAuthenticationMethodRequest, + EnrollmentChallengeOptions, + EnrollmentChallengeResponse, + EnrollmentVerifyOptions +} from './types'; + +/** + * Client for Auth0 MyAccount API operations. + * + * Provides methods for managing the current user's account: + * - Connected accounts (link/unlink external identity providers) + * - MFA factors + * - Authentication methods (passkeys, phone, email, TOTP) - list, get, update, delete + * - Authentication method enrollment (challenge and verify) + */ +export class MyAccountApiClient { + constructor( + private myAccountFetcher: Fetcher, + private apiBase: string + ) {} + + /** + * Get a ticket for the connect account flow. + * + * @param params - Connection parameters including connection name and redirect URI + * @returns A promise that resolves to a connect response with the redirect URI and auth session + */ + async connectAccount(params: ConnectRequest): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/connected-accounts/connect`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params) + }, + { scope: ['create:me:connected_accounts'] } + ); + return this._handleResponse(res); + } + + /** + * Verify the redirect from the connect account flow and complete the connecting of the account. + * + * @param params - Completion parameters including auth session, connect code, and redirect URI + * @returns A promise that resolves to the completed connected account details + */ + async completeAccount(params: CompleteRequest): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/connected-accounts/complete`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params) + }, + { scope: ['create:me:connected_accounts'] } + ); + return this._handleResponse(res); + } + + /** + * Get the status of all factors for the current user. + * + * @returns A promise that resolves to an array of factors with their enabled/disabled status + */ + async getFactors(): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/factors`, + { method: 'GET' }, + { scope: ['read:me:factors'] } + ); + const body = await this._handleResponse<{ factors: Factor[] }>(res); + return body.factors; + } + + /** + * Get a list of all authentication methods for the current user. + * + * @param type - Optional filter to return only methods of a specific type + * @returns A promise that resolves to an array of authentication methods + */ + async getAuthenticationMethods(type?: AuthenticationMethodType): Promise { + const query = type ? `?${new URLSearchParams({ type })}` : ''; + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/authentication-methods${query}`, + { method: 'GET' }, + { scope: ['read:me:authentication_methods'] } + ); + const body = await this._handleResponse<{ authentication_methods: AuthenticationMethod[] }>(res); + return body.authentication_methods; + } + + /** + * Get an authentication method by ID. + * + * @param id - The ID of the authentication method to retrieve + * @returns A promise that resolves to the authentication method + */ + async getAuthenticationMethod(id: string): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/authentication-methods/${encodeURIComponent(id)}`, + { method: 'GET' }, + { scope: ['read:me:authentication_methods'] } + ); + return this._handleResponse(res); + } + + /** + * Delete an authentication method by ID. + * + * @param id - The ID of the authentication method to delete + * @returns A promise that resolves when the authentication method has been deleted + */ + async deleteAuthenticationMethod(id: string): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/authentication-methods/${encodeURIComponent(id)}`, + { method: 'DELETE' }, + { scope: ['delete:me:authentication_methods'] } + ); + if (!res.ok) { + await this._handleResponse(res); + } + } + + /** + * Update details of an authentication method by ID. + * + * @param id - The ID of the authentication method to update + * @param data - The fields to update (e.g. name, preferred_authentication_method for phone) + * @returns A promise that resolves to the updated authentication method + */ + async updateAuthenticationMethod( + id: string, + data: UpdateAuthenticationMethodRequest + ): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/authentication-methods/${encodeURIComponent(id)}`, + { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }, + { scope: ['update:me:authentication_methods'] } + ); + return this._handleResponse(res); + } + + /** + * Start the enrollment of an authentication method for the current user. + * The response shape varies by type. + * + * @param options - Enrollment challenge options specifying the factor type and any type-specific fields + * @returns A promise that resolves to the enrollment challenge response (shape varies by type) + */ + async enrollmentChallenge( + options: EnrollmentChallengeOptions + ): Promise { + const res = await this.myAccountFetcher.fetchWithAuth( + `${this.apiBase}v1/authentication-methods`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(options) + }, + { scope: ['create:me:authentication_methods'] } + ); + + const raw = await this._handleResponse(res); + const location = res.headers.get('location') ?? ''; + const id = decodeURIComponent(location.split('/').pop() || ''); + return { ...raw, id, location }; + } + + /** + * Confirm the enrollment of an authentication method to complete registration. + * + * @param options - Enrollment verify options including the auth session and type-specific verification data + * @returns A promise that resolves to the confirmed authentication method + */ + async enrollmentVerify(options: EnrollmentVerifyOptions): Promise { + const { location, type: _, ...body } = options as any; + + const res = await this.myAccountFetcher.fetchWithAuth( + `${location}/verify`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }, + { scope: ['create:me:authentication_methods'] } + ); + return this._handleResponse(res); + } + + private async _handleResponse(res: Response): Promise { + let body: any; + try { + body = await res.text(); + body = JSON.parse(body); + } catch (err) { + throw new MyAccountApiError({ + type: 'invalid_json', + status: res.status, + title: 'Invalid JSON response', + detail: body || String(err) + }); + } + + if (res.ok) { + return body; + } else { + throw new MyAccountApiError(body); + } + } +} + +/** + * Error thrown when the MyAccount API returns a non-2xx response. + * + * @example + * ```typescript + * try { + * await myAccount.getAuthenticationMethods(); + * } catch (err) { + * if (err instanceof MyAccountApiError) { + * console.error(err.status, err.title, err.detail); + * } + * } + * ``` + */ +export class MyAccountApiError extends Error { + /** RFC 7807 error type identifier */ + public readonly type: string; + /** HTTP status code */ + public readonly status: number; + /** Short human-readable summary of the error */ + public readonly title: string; + /** Human-readable explanation specific to this occurrence */ + public readonly detail: string; + /** Field-level validation errors, if present */ + public readonly validation_errors?: ErrorResponse['validation_errors']; + + constructor({ + type, + status, + title, + detail, + validation_errors + }: ErrorResponse) { + super(detail); + this.name = 'MyAccountApiError'; + this.type = type; + this.status = status; + this.title = title; + this.detail = detail; + this.validation_errors = validation_errors; + Object.setPrototypeOf(this, MyAccountApiError.prototype); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/index.ts b/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/index.ts new file mode 100644 index 00000000..a3282656 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/index.ts @@ -0,0 +1,46 @@ +export { MyAccountApiClient, MyAccountApiError } from './MyAccountApiClient'; +export type { + ConnectRequest, + ConnectResponse, + CompleteRequest, + CompleteResponse, + ErrorResponse, + Factor, + AuthenticationMethodType, + AuthenticationMethod, + PasskeyAuthenticationMethod, + WebAuthnPlatformAuthenticationMethod, + WebAuthnRoamingAuthenticationMethod, + PhoneAuthenticationMethod, + EmailAuthenticationMethod, + PasswordAuthenticationMethod, + TotpAuthenticationMethod, + PushNotificationAuthenticationMethod, + RecoveryCodeAuthenticationMethod, + UpdateAuthenticationMethodRequest, + EnrollmentChallengeOptions, + PasskeyEnrollmentChallengeOptions, + PhoneEnrollmentChallengeOptions, + EmailEnrollmentChallengeOptions, + TotpEnrollmentChallengeOptions, + PushNotificationEnrollmentChallengeOptions, + RecoveryCodeEnrollmentChallengeOptions, + PasswordEnrollmentChallengeOptions, + EnrollmentChallengeResponse, + PasskeyEnrollmentChallengeResponse, + PhoneEnrollmentChallengeResponse, + EmailEnrollmentChallengeResponse, + TotpEnrollmentChallengeResponse, + PushNotificationEnrollmentChallengeResponse, + RecoveryCodeEnrollmentChallengeResponse, + PasswordEnrollmentChallengeResponse, + PasswordPolicy, + EnrollmentVerifyOptions, + PasskeyEnrollmentVerifyOptions, + PhoneEnrollmentVerifyOptions, + EmailEnrollmentVerifyOptions, + TotpEnrollmentVerifyOptions, + PushNotificationEnrollmentVerifyOptions, + RecoveryCodeEnrollmentVerifyOptions, + PasswordEnrollmentVerifyOptions +} from './types'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/types.ts b/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/types.ts new file mode 100644 index 00000000..d579702d --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/myaccount/types.ts @@ -0,0 +1,355 @@ +import type { AuthorizationParams } from '../global'; +import type { + PasskeyCreationOptions, + PasskeyCredentialResponse +} from '@auth0/auth0-auth-js'; + +// ─── Connected Accounts ────────────────────────────────────────────────────── + +export interface ConnectRequest { + connection: string; + scopes?: string[]; + redirect_uri: string; + state?: string; + code_challenge?: string; + code_challenge_method?: 'S256'; + authorization_params?: AuthorizationParams; +} + +export interface ConnectResponse { + connect_uri: string; + auth_session: string; + connect_params: { + ticket: string; + }; + expires_in: number; +} + +export interface CompleteRequest { + auth_session: string; + connect_code: string; + redirect_uri: string; + code_verifier?: string; +} + +export interface CompleteResponse { + id: string; + connection: string; + access_type: 'offline'; + scopes?: string[]; + created_at: string; + expires_at?: string; +} + +// ─── Error ─────────────────────────────────────────────────────────────────── + +export interface ErrorResponse { + type: string; + status: number; + title: string; + detail: string; + validation_errors?: { + detail: string; + field?: string; + pointer?: string; + source?: string; + }[]; +} + +// ─── Factors ───────────────────────────────────────────────────────────────── + +export type AuthenticationMethodType = + | 'passkey' + | 'password' + | 'phone' + | 'totp' + | 'email' + | 'push-notification' + | 'recovery-code' + | 'webauthn-platform' + | 'webauthn-roaming'; + +export interface Factor { + type: AuthenticationMethodType; + usage?: ('primary' | 'secondary')[]; +} + +// ─── Authentication Methods ─────────────────────────────────────────────────── + +interface AuthenticationMethodBase { + id: string; + created_at: string; + usage: ('primary' | 'secondary')[]; +} + +interface AuthenticationMethodWithMetadata extends AuthenticationMethodBase { + confirmed: boolean; + name?: string; + last_auth_at?: string; +} + +interface WebAuthnAuthenticationMethodBase + extends AuthenticationMethodWithMetadata { + key_id?: string; + public_key?: string; +} + +export interface PasskeyAuthenticationMethod extends AuthenticationMethodBase { + type: 'passkey'; + key_id: string; + public_key: string; + credential_device_type: 'single_device' | 'multi_device'; + credential_backed_up: boolean; + identity_user_id: string; + user_handle: string; + transports?: string[]; + user_agent?: string; + aaguid?: string; + relying_party_id?: string; + last_auth_at?: string; +} + +export interface WebAuthnPlatformAuthenticationMethod + extends WebAuthnAuthenticationMethodBase { + type: 'webauthn-platform'; +} + +export interface WebAuthnRoamingAuthenticationMethod + extends WebAuthnAuthenticationMethodBase { + type: 'webauthn-roaming'; +} + +export interface PhoneAuthenticationMethod + extends AuthenticationMethodWithMetadata { + type: 'phone'; + phone_number: string; + preferred_authentication_method: 'sms' | 'voice'; +} + +export interface EmailAuthenticationMethod + extends AuthenticationMethodWithMetadata { + type: 'email'; + email: string; +} + +export interface PasswordAuthenticationMethod extends AuthenticationMethodBase { + type: 'password'; + identity_user_id: string; + last_password_reset?: string; +} + +export interface TotpAuthenticationMethod + extends AuthenticationMethodWithMetadata { + type: 'totp'; +} + +export interface PushNotificationAuthenticationMethod + extends AuthenticationMethodWithMetadata { + type: 'push-notification'; +} + +export interface RecoveryCodeAuthenticationMethod + extends Omit { + type: 'recovery-code'; +} + +export type AuthenticationMethod = + | PasskeyAuthenticationMethod + | WebAuthnPlatformAuthenticationMethod + | WebAuthnRoamingAuthenticationMethod + | PhoneAuthenticationMethod + | EmailAuthenticationMethod + | PasswordAuthenticationMethod + | TotpAuthenticationMethod + | PushNotificationAuthenticationMethod + | RecoveryCodeAuthenticationMethod; + +export interface UpdateAuthenticationMethodRequest { + name?: string; + /** Only valid when updating a phone authentication method. */ + preferred_authentication_method?: 'sms' | 'voice'; +} + +// ─── Enrollment Challenge Options ──────────────────────────────────────────── + +export interface PasskeyEnrollmentChallengeOptions { + type: 'passkey'; + connection?: string; + identity_user_id?: string; +} + +export interface PhoneEnrollmentChallengeOptions { + type: 'phone'; + phone_number: string; + preferred_authentication_method?: 'sms' | 'voice'; +} + +export interface EmailEnrollmentChallengeOptions { + type: 'email'; + email: string; +} + +export interface TotpEnrollmentChallengeOptions { + type: 'totp'; +} + +export interface PushNotificationEnrollmentChallengeOptions { + type: 'push-notification'; +} + +export interface RecoveryCodeEnrollmentChallengeOptions { + type: 'recovery-code'; +} + +export interface PasswordEnrollmentChallengeOptions { + type: 'password'; + connection?: string; + identity_user_id?: string; +} + +export type EnrollmentChallengeOptions = + | PasskeyEnrollmentChallengeOptions + | PhoneEnrollmentChallengeOptions + | EmailEnrollmentChallengeOptions + | TotpEnrollmentChallengeOptions + | PushNotificationEnrollmentChallengeOptions + | RecoveryCodeEnrollmentChallengeOptions + | PasswordEnrollmentChallengeOptions; + +// ─── Enrollment Challenge Response ─────────────────────────────────────────── + +interface EnrollmentChallengeBaseResponse { + id: string; + location: string; + auth_session: string; +} + +export interface PasskeyEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'passkey'; + authn_params_public_key: PasskeyCreationOptions; +} + +export interface PhoneEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'phone'; +} + +export interface EmailEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'email'; +} + +export interface TotpEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'totp'; + barcode_uri: string; + manual_input_code?: string; +} + +export interface PushNotificationEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'push-notification'; + barcode_uri: string; + manual_input_code?: string; +} + +export interface RecoveryCodeEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'recovery-code'; + recovery_code: string; +} + +export interface PasswordPolicy { + complexity: { + min_length: number; + character_types: ('uppercase' | 'lowercase' | 'number' | 'special')[]; + character_type_rule: 'all' | 'three_of_four'; + identical_characters: 'allow' | 'block'; + sequential_characters: 'allow' | 'block'; + max_length_exceeded: 'truncate' | 'error'; + }; + profile_data: { + active: boolean; + blocked_fields: string[]; + }; + history: { + active: boolean; + size: number; + }; + dictionary: { + active: boolean; + default: 'en_10k' | 'en_100k'; + }; +} + +export interface PasswordEnrollmentChallengeResponse + extends EnrollmentChallengeBaseResponse { + type: 'password'; + policy: PasswordPolicy; +} + +export type EnrollmentChallengeResponse = + | PasskeyEnrollmentChallengeResponse + | PhoneEnrollmentChallengeResponse + | EmailEnrollmentChallengeResponse + | TotpEnrollmentChallengeResponse + | PushNotificationEnrollmentChallengeResponse + | RecoveryCodeEnrollmentChallengeResponse + | PasswordEnrollmentChallengeResponse; + +// ─── Enrollment Verify ──────────────────────────────────────────────────────── + +interface EnrollmentVerifyBaseOptions { + location: string; + auth_session: string; +} + +export interface PasskeyEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'passkey'; + authn_response: PasskeyCredentialResponse; +} + +export interface PhoneEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'phone'; + otp_code: string; +} + +export interface EmailEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'email'; + otp_code: string; +} + +export interface TotpEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'totp'; + otp_code: string; +} + +export interface PushNotificationEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'push-notification'; +} + +export interface RecoveryCodeEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'recovery-code'; +} + +export interface PasswordEnrollmentVerifyOptions + extends EnrollmentVerifyBaseOptions { + type: 'password'; + new_password: string; +} + +export type EnrollmentVerifyOptions = + | PasskeyEnrollmentVerifyOptions + | PhoneEnrollmentVerifyOptions + | EmailEnrollmentVerifyOptions + | TotpEnrollmentVerifyOptions + | PushNotificationEnrollmentVerifyOptions + | RecoveryCodeEnrollmentVerifyOptions + | PasswordEnrollmentVerifyOptions; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/passkey/PasskeyApiClient.ts b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/PasskeyApiClient.ts new file mode 100644 index 00000000..1c3a8030 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/PasskeyApiClient.ts @@ -0,0 +1,332 @@ +import type { Auth0Client } from '../Auth0Client'; +import type { TokenEndpointResponse } from '../global'; +import type { + PasskeySignupOptions, + PasskeyLoginOptions, + PasskeyCredentialResponse, + PasskeyCreationOptions, + PasskeyRequestOptions, + PasskeySignupChallengeOptions, + PasskeyLoginChallengeOptions, + PasskeySignupChallenge, + PasskeyLoginChallenge, + PasskeyGetTokenOptions +} from './types'; +import type { PasskeyClient } from '@auth0/auth0-auth-js'; +import { PasskeyError } from './errors'; + +/** + * Client for Auth0 Passkey operations. + * + * Provides 2 public methods: + * - `signup` — Register a new user with a passkey (full flow: challenge → WebAuthn → token exchange) + * - `login` — Sign in with a passkey (full flow: challenge → WebAuthn → token exchange) + * + * @example + * ```typescript + * // Signup — single call handles everything + * const tokens = await auth0.passkey.signup({ email: 'user@example.com' }); + * + * // Login — single call handles everything + * const tokens = await auth0.passkey.login(); + * ``` + */ +export class PasskeyApiClient { + #passkeyClient: PasskeyClient; + #auth0Client: Auth0Client; + + /** + * @internal + * Do not instantiate directly. Use Auth0Client.passkey instead. + */ + constructor(passkeyClient: PasskeyClient, auth0Client: Auth0Client) { + this.#passkeyClient = passkeyClient; + this.#auth0Client = auth0Client; + } + + /** + * Register a new user with a passkey. + * + * Handles the full flow: requests a signup challenge, triggers the browser + * WebAuthn credential creation ceremony, serializes the result, and exchanges + * it for tokens. + * + * @param options - Passkey signup options (user identifier, optional scope/audience) + * @returns A promise that resolves to the token endpoint response containing access/ID tokens + * @throws {PasskeyError} If WebAuthn is not supported in the browser + * @throws {PasskeyRegisterError} If the challenge request fails + * @throws {GenericError} If the token exchange fails + * @throws {PasskeyError} If the user cancels the WebAuthn prompt + */ + async signup(options: PasskeySignupOptions): Promise { + if (!window.PublicKeyCredential) { + throw new PasskeyError('passkey_not_supported', 'WebAuthn is not supported in this browser.'); + } + + const { scope, audience, ...challengeOptions } = options; + + const challenge = await this.#passkeyClient.register(challengeOptions); + + const publicKeyOptions = prepareCreationOptions( + challenge.authnParamsPublicKey + ); + const credential = await navigator.credentials.create({ + publicKey: publicKeyOptions + }); + + if (!credential) { + throw new PasskeyError( + 'passkey_cancelled', + 'Passkey creation was cancelled or no credential was returned.' + ); + } + + const serialized = serializeCreationCredential( + credential as PublicKeyCredential + ); + + return this.#auth0Client._requestTokenForPasskey({ + authSession: challenge.authSession, + credential: serialized, + realm: challengeOptions.realm, + organization: challengeOptions.organization, + scope, + audience + }); + } + + /** + * Sign in with an existing passkey. + * + * Handles the full flow: requests a login challenge, triggers the browser + * WebAuthn assertion ceremony, serializes the result, and exchanges it + * for tokens. + * + * @param options - Optional passkey login options (optional scope/audience/realm/organization) + * @returns A promise that resolves to the token endpoint response containing access/ID tokens + * @throws {PasskeyError} If WebAuthn is not supported in the browser + * @throws {PasskeyChallengeError} If the challenge request fails + * @throws {GenericError} If the token exchange fails + * @throws {PasskeyError} If the user cancels the WebAuthn prompt + */ + async login(options?: PasskeyLoginOptions): Promise { + if (!window.PublicKeyCredential) { + throw new PasskeyError('passkey_not_supported', 'WebAuthn is not supported in this browser.'); + } + + const { scope, audience, ...challengeOptions } = options || {}; + + const challenge = await this.#passkeyClient.challenge( + Object.keys(challengeOptions).length > 0 ? challengeOptions : undefined + ); + + const publicKeyOptions = prepareRequestOptions( + challenge.authnParamsPublicKey + ); + const credential = await navigator.credentials.get({ + publicKey: publicKeyOptions + }); + + if (!credential) { + throw new PasskeyError( + 'passkey_cancelled', + 'Passkey authentication was cancelled or no credential was returned.' + ); + } + + const serialized = serializeAssertionCredential( + credential as PublicKeyCredential + ); + + return this.#auth0Client._requestTokenForPasskey({ + authSession: challenge.authSession, + credential: serialized, + realm: challengeOptions.realm, + organization: challengeOptions.organization, + scope, + audience + }); + } + + /** + * Request a passkey signup challenge. + * + * Step 1 of the granular signup flow. Returns the auth session and a + * decoded `PublicKeyCredentialCreationOptions`. Run the WebAuthn credential + * creation ceremony with `publicKey`, then hand the resulting credential and `authSession` to + * `getTokenWithPasskey()`. + * + * @param options - Signup challenge options (user identifier, optional realm/organization/metadata) + * @returns A promise resolving to `{ authSession, publicKey }` + * @throws {PasskeyError} If WebAuthn is not supported in the browser + * @throws {PasskeyRegisterError} If the challenge request fails + */ + async getSignupChallenge( + options: PasskeySignupChallengeOptions + ): Promise { + if (!window.PublicKeyCredential) { + throw new PasskeyError('passkey_not_supported', 'WebAuthn is not supported in this browser.'); + } + + const challenge = await this.#passkeyClient.register(options); + return { + authSession: challenge.authSession, + publicKey: prepareCreationOptions(challenge.authnParamsPublicKey) + }; + } + + /** + * Request a passkey login challenge. + * + * Step 1 of the granular login flow. Returns the auth session and a + * decoded `PublicKeyCredentialRequestOptions`. Run the WebAuthn assertion + * ceremony with `publicKey`, then hand the resulting credential and `authSession` to + * `getTokenWithPasskey()`. + * + * @param options - Optional login challenge options (realm/organization) + * @returns A promise resolving to `{ authSession, publicKey }` + * @throws {PasskeyError} If WebAuthn is not supported in the browser + * @throws {PasskeyChallengeError} If the challenge request fails + */ + async getLoginChallenge( + options?: PasskeyLoginChallengeOptions + ): Promise { + if (!window.PublicKeyCredential) { + throw new PasskeyError('passkey_not_supported', 'WebAuthn is not supported in this browser.'); + } + + const challenge = await this.#passkeyClient.challenge(options); + + return { + authSession: challenge.authSession, + publicKey: prepareRequestOptions(challenge.authnParamsPublicKey) + }; + } + + /** + * Exchange a signed passkey credential for tokens. + * + * Step 2 of the granular flow. Serializes the raw `PublicKeyCredential` + * produced by the WebAuthn ceremony — either a creation (signup) or an + * assertion (login) credential — and exchanges it for tokens. The credential + * type (attestation vs assertion) is detected automatically. + * + * @param options - The auth session, raw credential, and optional realm/organization/scope/audience + * @returns A promise resolving to the token endpoint response + * @throws {PasskeyError} If WebAuthn is not supported in the browser + * @throws {PasskeyError} If the credential is not a valid attestation or assertion response + * @throws {GenericError} If the token exchange fails + */ + async getTokenWithPasskey( + options: PasskeyGetTokenOptions + ): Promise { + if (!window.PublicKeyCredential) { + throw new PasskeyError('passkey_not_supported', 'WebAuthn is not supported in this browser.'); + } + + const { authSession, credential, realm, organization, scope, audience } = options; + const response = credential.response; + + let serialized: PasskeyCredentialResponse; + + if (response instanceof AuthenticatorAttestationResponse) { + serialized = serializeCreationCredential(credential); + } else if (response instanceof AuthenticatorAssertionResponse) { + serialized = serializeAssertionCredential(credential); + } else { + throw new PasskeyError( + 'passkey_invalid_credential', + 'The provided credential is not a valid attestation or assertion response.' + ); + } + + return this.#auth0Client._requestTokenForPasskey({ + authSession, + credential: serialized, + realm, + organization, + scope, + audience + }); + } + +} + +function bufferToBase64url(buffer: ArrayBuffer): string { + const bytes = new Uint8Array(buffer); + const binary = Array.from(bytes, b => String.fromCharCode(b)).join(''); + return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} + +function base64urlToBuffer(base64url: string): ArrayBuffer { + const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/'); + const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4); + const binary = atob(padded); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes.buffer; +} + +function prepareCreationOptions( + publicKey: PasskeyCreationOptions +): PublicKeyCredentialCreationOptions { + return { + ...publicKey, + challenge: base64urlToBuffer(publicKey.challenge), + user: { + ...publicKey.user, + id: base64urlToBuffer(publicKey.user.id) + }, + pubKeyCredParams: publicKey.pubKeyCredParams as PublicKeyCredentialParameters[], + authenticatorSelection: publicKey.authenticatorSelection as AuthenticatorSelectionCriteria | undefined + }; +} + +function prepareRequestOptions( + publicKey: PasskeyRequestOptions +): PublicKeyCredentialRequestOptions { + return { + ...publicKey, + challenge: base64urlToBuffer(publicKey.challenge) + } as PublicKeyCredentialRequestOptions; +} + +function serializeCreationCredential( + credential: PublicKeyCredential +): PasskeyCredentialResponse { + const response = credential.response as AuthenticatorAttestationResponse; + return { + id: credential.id, + rawId: bufferToBase64url(credential.rawId), + type: credential.type, + authenticatorAttachment: credential.authenticatorAttachment ?? undefined, + response: { + clientDataJSON: bufferToBase64url(response.clientDataJSON), + attestationObject: bufferToBase64url(response.attestationObject) + }, + clientExtensionResults: credential.getClientExtensionResults() as Record + }; +} + +function serializeAssertionCredential( + credential: PublicKeyCredential +): PasskeyCredentialResponse { + const response = credential.response as AuthenticatorAssertionResponse; + return { + id: credential.id, + rawId: bufferToBase64url(credential.rawId), + type: credential.type, + authenticatorAttachment: credential.authenticatorAttachment ?? undefined, + response: { + clientDataJSON: bufferToBase64url(response.clientDataJSON), + authenticatorData: bufferToBase64url(response.authenticatorData), + signature: bufferToBase64url(response.signature), + userHandle: response.userHandle + ? bufferToBase64url(response.userHandle) + : undefined + }, + clientExtensionResults: credential.getClientExtensionResults() as Record + }; +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/passkey/errors.ts b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/errors.ts new file mode 100644 index 00000000..8d25f760 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/errors.ts @@ -0,0 +1,25 @@ +export { + PasskeyRegisterError, + PasskeyChallengeError, + PasskeyGetTokenError +} from '@auth0/auth0-auth-js'; + +export interface PasskeyErrorResponse { + error: string; + error_description: string; + message?: string; +} + +export class PasskeyError extends Error { + public readonly code: string; + public readonly cause?: PasskeyErrorResponse; + + constructor(code: string, message: string, cause?: PasskeyErrorResponse) { + super(message); + this.name = 'PasskeyError'; + this.code = code; + this.cause = cause; + Object.setPrototypeOf(this, PasskeyError.prototype); + } +} + diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/passkey/index.ts b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/index.ts new file mode 100644 index 00000000..09796a60 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/index.ts @@ -0,0 +1,22 @@ +export { PasskeyApiClient } from './PasskeyApiClient'; +export { + PasskeyError, + PasskeyRegisterError, + PasskeyChallengeError, + PasskeyGetTokenError +} from './errors'; +export type { PasskeyErrorResponse } from './errors'; +export type { + PasskeySignupChallengeOptions, + PasskeySignupChallengeResponse, + PasskeyLoginChallengeOptions, + PasskeyLoginChallengeResponse, + PasskeyCredentialResponse, + PasskeyCreationOptions, + PasskeyRequestOptions, + PasskeySignupOptions, + PasskeyLoginOptions, + PasskeySignupChallenge, + PasskeyLoginChallenge, + PasskeyGetTokenOptions +} from './types'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/passkey/types.ts b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/types.ts new file mode 100644 index 00000000..eb315bae --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/passkey/types.ts @@ -0,0 +1,74 @@ +import type { + PasskeySignupChallengeOptions, + PasskeySignupChallengeResponse, + PasskeyLoginChallengeOptions, + PasskeyLoginChallengeResponse, + PasskeyCredentialResponse, + PasskeyCreationOptions, + PasskeyRequestOptions +} from '@auth0/auth0-auth-js'; + +export type { + PasskeySignupChallengeOptions, + PasskeySignupChallengeResponse, + PasskeyLoginChallengeOptions, + PasskeyLoginChallengeResponse, + PasskeyCredentialResponse, + PasskeyCreationOptions, + PasskeyRequestOptions +}; + +/** + * Options for passkey signup (registering a new user with a passkey). + */ +export type PasskeySignupOptions = PasskeySignupChallengeOptions & { + scope?: string; + audience?: string; +}; + +/** + * Options for passkey login (authenticating with an existing passkey). + */ +export type PasskeyLoginOptions = PasskeyLoginChallengeOptions & { + scope?: string; + audience?: string; +}; + +/** + * A passkey signup challenge ready for the WebAuthn ceremony. + * + * `publicKey` is decoded (base64url → ArrayBuffer) and can be fed directly into + * a WebAuthn credential creation ceremony. `authSession` must be retained and passed to `getTokenWithPasskey()`. + */ +export type PasskeySignupChallenge = { + authSession: string; + publicKey: PublicKeyCredentialCreationOptions; +}; + +/** + * A passkey login challenge ready for the WebAuthn ceremony. + * + * `publicKey` is decoded (base64url → ArrayBuffer) and can be fed directly into + * a WebAuthn assertion ceremony . `authSession` must be retained and passed to `getTokenWithPasskey()`. + */ +export type PasskeyLoginChallenge = { + authSession: string; + publicKey: PublicKeyCredentialRequestOptions; +}; + +/** + * Options for exchanging a signed passkey credential for tokens. + * + * `credential` is the raw `PublicKeyCredential` produced by the WebAuthn + * ceremony — a creation credential (signup) or an assertion credential + * (login). The credential type (attestation vs assertion) is detected + * automatically during serialization. + */ +export type PasskeyGetTokenOptions = { + authSession: string; + credential: PublicKeyCredential; + realm?: string; + organization?: string; + scope?: string; + audience?: string; +}; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/promise-utils.ts b/backend/node_modules/@auth0/auth0-spa-js/src/promise-utils.ts new file mode 100644 index 00000000..6c5457e4 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/promise-utils.ts @@ -0,0 +1,29 @@ +const singlePromiseMap: Record> = {}; + +export const singlePromise = ( + cb: () => Promise, + key: string +): Promise => { + let promise: null | Promise = singlePromiseMap[key]; + if (!promise) { + promise = cb().finally(() => { + delete singlePromiseMap[key]; + promise = null; + }); + singlePromiseMap[key] = promise; + } + return promise; +}; + +export const retryPromise = async ( + cb: () => Promise, + maxNumberOfRetries = 3 +) => { + for (let i = 0; i < maxNumberOfRetries; i++) { + if (await cb()) { + return true; + } + } + + return false; +}; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/scope.ts b/backend/node_modules/@auth0/auth0-spa-js/src/scope.ts new file mode 100644 index 00000000..eea8ecc6 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/scope.ts @@ -0,0 +1,74 @@ +import { DEFAULT_AUDIENCE } from "./constants"; + +/** + * @ignore + */ +const dedupe = (arr: string[]) => Array.from(new Set(arr)); + +/** + * @ignore + */ +/** + * Returns a string of unique scopes by removing duplicates and unnecessary whitespace. + * + * @param {...(string | undefined)[]} scopes - A list of scope strings or undefined values. + * @returns {string} A string containing unique scopes separated by a single space. + */ +export const getUniqueScopes = (...scopes: (string | undefined)[]) => { + return dedupe(scopes.filter(Boolean).join(' ').trim().split(/\s+/)).join(' '); +}; + +/** + * @ignore + */ +/** + * We will check if the developer has created the client with a string or object of audience:scopes. We will inject + * the base scopes to each audience, and store the base ones inside default key. As well, if the developer created the Auth0Client + * with a string of scopes, we will store the requested ones with the base scopes inside the default key as well. + * @param authScopes The scopes requested by the user when creating the Auth0Client + * @param openIdScope openId scope + * @param extraScopes Other scopes to accumulate such as offline_access + * @returns {Record} An object with all scopes that are going to be accumulated. + */ +export const injectDefaultScopes = (authScopes: string | Record | undefined, openIdScope: string, ...extraScopes: string[]): Record => { + if (typeof authScopes !== 'object') { + return { [DEFAULT_AUDIENCE]: getUniqueScopes(openIdScope, authScopes, ...extraScopes) }; + } + + let requestedScopes: Record = { + [DEFAULT_AUDIENCE]: getUniqueScopes(openIdScope, ...extraScopes), + }; + + Object.keys(authScopes).forEach((key) => { + const audienceScopes = authScopes[key]; + + requestedScopes[key] = getUniqueScopes(openIdScope, audienceScopes, ...extraScopes); + }); + + return requestedScopes; +} + +/** + * @ignore + */ +/** + * Will return a string of scopes. If a specific audience was requested and it exist inside the scopes object, we will return those + * related to that audience that we want to accumulate. If not, we will return the ones stored inside the default key. + * @param authScopes Object of audience:scopes that are going to be accumulated + * @param methodScopes The scopes requested for the developer in a specific request + * @param audience The audience the developer requested for an specific request or the one they configured in the Auth0Client + * @returns {string} A combination of Auth0Client scopes and the ones requested by the developer for a specific request + */ +export const scopesToRequest = (authScopes: Record, methodScopes: string | undefined, audience: string | undefined): string => { + let scope: string | undefined; + + if (audience) { + scope = authScopes[audience]; + } + + if (!scope) { + scope = authScopes[DEFAULT_AUDIENCE]; + } + + return getUniqueScopes(scope, methodScopes); +} \ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/storage.ts b/backend/node_modules/@auth0/auth0-spa-js/src/storage.ts new file mode 100644 index 00000000..1b4bb6f7 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/storage.ts @@ -0,0 +1,145 @@ +import * as Cookies from 'es-cookie'; + +interface ClientStorageOptions { + daysUntilExpire?: number; + cookieDomain?: string; +} + +/** + * Defines a type that handles storage to/from a storage location + */ +export type ClientStorage = { + get(key: string): T | undefined; + save(key: string, value: any, options?: ClientStorageOptions): void; + remove(key: string, options?: ClientStorageOptions): void; +}; + +/** + * A storage protocol for marshalling data to/from cookies + */ +export const CookieStorage = { + get(key: string) { + const value = Cookies.get(key); + + if (typeof value === 'undefined') { + return; + } + + return JSON.parse(value); + }, + + save(key: string, value: any, options?: ClientStorageOptions): void { + let cookieAttributes: Cookies.CookieAttributes = {}; + + if ('https:' === window.location.protocol) { + cookieAttributes = { + secure: true, + sameSite: 'none' + }; + } + + if (options?.daysUntilExpire) { + cookieAttributes.expires = options.daysUntilExpire; + } + + if (options?.cookieDomain) { + cookieAttributes.domain = options.cookieDomain; + } + + Cookies.set(key, JSON.stringify(value), cookieAttributes); + }, + + remove(key: string, options?: ClientStorageOptions) { + let cookieAttributes: Cookies.CookieAttributes = {}; + + if (options?.cookieDomain) { + cookieAttributes.domain = options.cookieDomain; + } + + Cookies.remove(key, cookieAttributes); + } +} as ClientStorage; + +/** + * @ignore + */ +const LEGACY_PREFIX = '_legacy_'; + +/** + * Cookie storage that creates a cookie for modern and legacy browsers. + * See: https://web.dev/samesite-cookie-recipes/#handling-incompatible-clients + */ +export const CookieStorageWithLegacySameSite = { + get(key: string) { + const value = CookieStorage.get(key); + + if (value) { + return value; + } + + return CookieStorage.get(`${LEGACY_PREFIX}${key}`); + }, + + save(key: string, value: any, options?: ClientStorageOptions): void { + let cookieAttributes: Cookies.CookieAttributes = {}; + + if ('https:' === window.location.protocol) { + cookieAttributes = { secure: true }; + } + + if (options?.daysUntilExpire) { + cookieAttributes.expires = options.daysUntilExpire; + } + + if (options?.cookieDomain) { + cookieAttributes.domain = options.cookieDomain; + } + + Cookies.set( + `${LEGACY_PREFIX}${key}`, + JSON.stringify(value), + cookieAttributes + ); + CookieStorage.save(key, value, options); + }, + + remove(key: string, options?: ClientStorageOptions) { + let cookieAttributes: Cookies.CookieAttributes = {}; + + if (options?.cookieDomain) { + cookieAttributes.domain = options.cookieDomain; + } + + Cookies.remove(key, cookieAttributes); + CookieStorage.remove(key, options); + CookieStorage.remove(`${LEGACY_PREFIX}${key}`, options); + } +} as ClientStorage; + +/** + * A storage protocol for marshalling data to/from session storage + */ +export const SessionStorage = { + get(key: string) { + /* c8 ignore next 3 */ + if (typeof sessionStorage === 'undefined') { + return; + } + + const value = sessionStorage.getItem(key); + + if (value == null) { + return; + } + + return JSON.parse(value); + }, + + save(key: string, value: any): void { + sessionStorage.setItem(key, JSON.stringify(value)); + }, + + remove(key: string) { + sessionStorage.removeItem(key); + } +} as ClientStorage; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/transaction-manager.ts b/backend/node_modules/@auth0/auth0-spa-js/src/transaction-manager.ts new file mode 100644 index 00000000..f26bdf25 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/transaction-manager.ts @@ -0,0 +1,56 @@ +import { ClientStorage } from './storage'; + +const TRANSACTION_STORAGE_KEY_PREFIX = 'a0.spajs.txs'; + +export interface LoginTransaction { + nonce: string; + scope: string; + audience: string; + appState?: any; + code_verifier: string; + redirect_uri?: string; + organization?: string; + state?: string; + response_type: 'code'; +} + +export interface ConnectAccountTransaction { + appState?: any; + audience?: string; + auth_session: string; + code_verifier: string; + redirect_uri: string; + scope?: string; + state: string; + connection: string; + response_type: 'connect_code'; +} + +export class TransactionManager { + private storageKey: string; + + constructor( + private storage: ClientStorage, + private clientId: string, + private cookieDomain?: string + ) { + this.storageKey = `${TRANSACTION_STORAGE_KEY_PREFIX}.${this.clientId}`; + } + + public create(transaction: T) { + this.storage.save(this.storageKey, transaction, { + daysUntilExpire: 1, + cookieDomain: this.cookieDomain + }); + } + + public get(): T | undefined { + return this.storage.get(this.storageKey); + } + + public remove() { + this.storage.remove(this.storageKey, { + cookieDomain: this.cookieDomain + }); + } +} \ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/utils.ts b/backend/node_modules/@auth0/auth0-spa-js/src/utils.ts new file mode 100644 index 00000000..4273ffc1 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/utils.ts @@ -0,0 +1,316 @@ +import { AuthenticationResult, PopupConfigOptions } from './global'; + +import { + DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS, + CLEANUP_IFRAME_TIMEOUT_IN_SECONDS +} from './constants'; + +import { + PopupTimeoutError, + TimeoutError, + GenericError, + PopupCancelledError +} from './errors'; + +export const parseAuthenticationResult = ( + queryString: string +): AuthenticationResult => { + if (queryString.indexOf('#') > -1) { + queryString = queryString.substring(0, queryString.indexOf('#')); + } + + const searchParams = new URLSearchParams(queryString); + + return { + state: searchParams.get('state')!, + code: searchParams.get('code') || undefined, + connect_code: searchParams.get('connect_code') || undefined, + error: searchParams.get('error') || undefined, + error_description: searchParams.get('error_description') || undefined + }; +}; + +export const runIframe = ( + authorizeUrl: string, + eventOrigin: string, + timeoutInSeconds: number = DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS +) => { + return new Promise((res, rej) => { + const iframe = window.document.createElement('iframe'); + + iframe.setAttribute('width', '0'); + iframe.setAttribute('height', '0'); + iframe.style.display = 'none'; + + const removeIframe = () => { + if (window.document.body.contains(iframe)) { + window.document.body.removeChild(iframe); + window.removeEventListener('message', iframeEventHandler, false); + } + }; + + let iframeEventHandler: (e: MessageEvent) => void; + + const timeoutSetTimeoutId = setTimeout(() => { + rej(new TimeoutError()); + removeIframe(); + }, timeoutInSeconds * 1000); + + iframeEventHandler = function (e: MessageEvent) { + if (e.origin != eventOrigin) return; + if (!e.data || e.data.type !== 'authorization_response') return; + + const eventSource = e.source; + + if (eventSource) { + (eventSource as any).close(); + } + + e.data.response.error + ? rej(GenericError.fromPayload(e.data.response)) + : res(e.data.response); + + clearTimeout(timeoutSetTimeoutId); + window.removeEventListener('message', iframeEventHandler, false); + + // Delay the removal of the iframe to prevent hanging loading status + // in Chrome: https://github.com/auth0/auth0-spa-js/issues/240 + setTimeout(removeIframe, CLEANUP_IFRAME_TIMEOUT_IN_SECONDS * 1000); + }; + + window.addEventListener('message', iframeEventHandler, false); + window.document.body.appendChild(iframe); + iframe.setAttribute('src', authorizeUrl); + }); +}; + +export const openPopup = (url: string) => { + const width = 400; + const height = 600; + const left = window.screenX + (window.innerWidth - width) / 2; + const top = window.screenY + (window.innerHeight - height) / 2; + + return window.open( + url, + 'auth0:authorize:popup', + `left=${left},top=${top},width=${width},height=${height},resizable,scrollbars=yes,status=1` + ); +}; + +export const runPopup = (config: PopupConfigOptions, eventOrigin: string) => { + return new Promise((resolve, reject) => { + let popupEventListener: (e: MessageEvent) => void; + + // Check each second if the popup is closed triggering a PopupCancelledError + const popupTimer = setInterval(() => { + if (config.popup && config.popup.closed) { + clearInterval(popupTimer); + clearTimeout(timeoutId); + window.removeEventListener('message', popupEventListener, false); + reject(new PopupCancelledError(config.popup)); + } + }, 1000); + + const timeoutId = setTimeout(() => { + clearInterval(popupTimer); + reject(new PopupTimeoutError(config.popup)); + window.removeEventListener('message', popupEventListener, false); + }, (config.timeoutInSeconds || DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS) * 1000); + + popupEventListener = function (e: MessageEvent) { + if (e.origin !== eventOrigin) return; + if (!e.data || e.data.type !== 'authorization_response') { + return; + } + + clearTimeout(timeoutId); + clearInterval(popupTimer); + window.removeEventListener('message', popupEventListener, false); + + // Close popup automatically unless closePopup is explicitly set to false + if (config.closePopup !== false) { + config.popup.close(); + } + + if (e.data.response.error) { + return reject(GenericError.fromPayload(e.data.response)); + } + + resolve(e.data.response); + }; + + window.addEventListener('message', popupEventListener); + }); +}; + +export const getCrypto = () => { + return window.crypto; +}; + +export const createRandomString = () => { + const charset = + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.'; + const validMax = 256 - (256 % charset.length); + let random = ''; + while (random.length < 43) { + const bytes = getCrypto().getRandomValues(new Uint8Array(43 - random.length)); + for (const byte of bytes) { + if (random.length < 43 && byte < validMax) { + random += charset[byte % charset.length]; + } + } + } + return random; +}; + +export const encode = (value: string) => btoa(value); +export const decode = (value: string) => atob(value); + +const stripUndefined = (params: any) => { + return Object.keys(params) + .filter(k => typeof params[k] !== 'undefined') + .reduce((acc, key) => ({ ...acc, [key]: params[key] }), {}); +}; + +const ALLOWED_AUTH0CLIENT_PROPERTIES = [ + { + key: 'name', + type: ['string'] + }, + { + key: 'version', + type: ['string', 'number'] + }, + { + key: 'env', + type: ['object'] + } +]; + +/** + * Strips any property that is not present in ALLOWED_AUTH0CLIENT_PROPERTIES + * @param auth0Client - The full auth0Client object + * @param excludeEnv - If true, excludes the 'env' property from the result + * @returns The stripped auth0Client object + */ +export const stripAuth0Client = (auth0Client: any, excludeEnv = false) => { + return Object.keys(auth0Client).reduce((acc: any, key: string) => { + // Exclude 'env' if requested (for /authorize query params to prevent truncation) + if (excludeEnv && key === 'env') { + return acc; + } + + const allowedProperty = ALLOWED_AUTH0CLIENT_PROPERTIES.find( + p => p.key === key + ); + if ( + allowedProperty && + allowedProperty.type.includes(typeof auth0Client[key]) + ) { + acc[key] = auth0Client[key]; + } + + return acc; + }, {}); +}; + +export const createQueryParams = ({ clientId: client_id, ...params }: any) => { + return new URLSearchParams( + stripUndefined({ client_id, ...params }) + ).toString(); +}; + +export const sha256 = async (s: string) => { + const digestOp: any = getCrypto().subtle.digest( + { name: 'SHA-256' }, + new TextEncoder().encode(s) + ); + + return await digestOp; +}; + +const urlEncodeB64 = (input: string) => { + const b64Chars: { [index: string]: string } = { '+': '-', '/': '_', '=': '' }; + return input.replace(/[+/=]/g, (m: string) => b64Chars[m]); +}; + +// https://stackoverflow.com/questions/30106476/ +const decodeB64 = (input: string) => + decodeURIComponent( + atob(input) + .split('') + .map(c => { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join('') + ); + +export const urlDecodeB64 = (input: string) => + decodeB64(input.replace(/_/g, '/').replace(/-/g, '+')); + +export const bufferToBase64UrlEncoded = (input: number[] | Uint8Array) => { + const ie11SafeInput = new Uint8Array(input); + return urlEncodeB64( + window.btoa(String.fromCharCode(...Array.from(ie11SafeInput))) + ); +}; + +export const validateCrypto = () => { + if (!getCrypto()) { + throw new Error( + 'For security reasons, `window.crypto` is required to run `auth0-spa-js`.' + ); + } + if (typeof getCrypto().subtle === 'undefined') { + throw new Error(` + auth0-spa-js must run on a secure origin. See https://github.com/auth0/auth0-spa-js/blob/main/FAQ.md#why-do-i-get-auth0-spa-js-must-run-on-a-secure-origin for more information. + `); + } +}; + +/** + * @ignore + */ +export const getDomain = (domainUrl: string) => { + if (!/^https?:\/\//.test(domainUrl)) { + return `https://${domainUrl}`; + } + + return domainUrl; +}; + +/** + * @ignore + */ +export const getTokenIssuer = ( + issuer: string | undefined, + domainUrl: string +) => { + if (issuer) { + return issuer.startsWith('https://') ? issuer : `https://${issuer}/`; + } + + return `${domainUrl}/`; +}; + +export const parseNumber = (value: any): number | undefined => { + if (typeof value !== 'string') { + return value; + } + return parseInt(value, 10) || undefined; +}; + +/** + * Ponyfill for `Object.fromEntries()`, which is not available until ES2020. + * + * When the target of this project reaches ES2020, this can be removed. + */ +export const fromEntries = ( + iterable: Iterable<[PropertyKey, T]> +): Record => { + return [...iterable].reduce((obj, [key, val]) => { + obj[key] = val; + + return obj; + }, {} as Record); +}; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/version.ts b/backend/node_modules/@auth0/auth0-spa-js/src/version.ts new file mode 100644 index 00000000..599373a9 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/version.ts @@ -0,0 +1 @@ +export default '2.24.1'; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/worker/__mocks__/token.worker.ts b/backend/node_modules/@auth0/auth0-spa-js/src/worker/__mocks__/token.worker.ts new file mode 100644 index 00000000..d13f094d --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/worker/__mocks__/token.worker.ts @@ -0,0 +1,10 @@ +const { messageRouter } = jest.requireActual('../token.worker'); + +export default class { + postMessage(data, ports) { + messageRouter({ + data, + ports: ports || [] + }); + } +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/worker/token.worker.ts b/backend/node_modules/@auth0/auth0-spa-js/src/worker/token.worker.ts new file mode 100644 index 00000000..b9d4ab09 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/worker/token.worker.ts @@ -0,0 +1,376 @@ +import { MissingRefreshTokenError } from '../errors'; +import { FetchResponse } from '../global'; +import { createQueryParams, fromEntries } from '../utils'; +import { + WorkerMessage, + WorkerRefreshTokenMessage, + WorkerRevokeTokenMessage +} from './worker.types'; + +let refreshTokens: Record = {}; +let allowedBaseUrl: string | null = null; + +const cacheKey = (audience: string, scope: string) => `${audience}|${scope}`; + +const cacheKeyContainsAudience = (audience: string, cacheKey: string) => cacheKey.startsWith(`${audience}|`); + +const getRefreshToken = (audience: string, scope: string): string | undefined => + refreshTokens[cacheKey(audience, scope)]; + +const setRefreshToken = ( + refreshToken: string, + audience: string, + scope: string +) => (refreshTokens[cacheKey(audience, scope)] = refreshToken); + +const deleteRefreshToken = (audience: string, scope: string) => + delete refreshTokens[cacheKey(audience, scope)]; + +const getRefreshTokensByAudience = (audience: string): string[] => { + const seen = new Set(); + Object.entries(refreshTokens).forEach(([key, token]) => { + if (cacheKeyContainsAudience(audience, key)) { + seen.add(token); + } + }); + return Array.from(seen); +}; + +const deleteRefreshTokensByValue = (refreshToken: string): void => { + Object.entries(refreshTokens).forEach(([key, token]) => { + if (token === refreshToken) { + delete refreshTokens[key]; + } + }); +}; + +const wait = (time: number) => + new Promise(resolve => setTimeout(resolve, time)); + +const formDataToObject = (formData: string): Record => { + const queryParams = new URLSearchParams(formData); + const parsedQuery: any = {}; + + queryParams.forEach((val, key) => { + parsedQuery[key] = val; + }); + + return parsedQuery; +}; + +const updateRefreshTokens = (oldRefreshToken: string | undefined, newRefreshToken: string): void => { + Object.entries(refreshTokens).forEach(([key, token]) => { + if (token === oldRefreshToken) { + refreshTokens[key] = newRefreshToken; + } + }); +} + +const checkDownscoping = (scope: string, audience: string): boolean => { + 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 ({ + data: { timeout, auth, fetchUrl, fetchOptions, useFormData, useMrrt, skipTokenStorage, preserveRefreshToken }, + ports: [port] +}: MessageEvent) => { + let headers: FetchResponse['headers'] = {}; + + let json: { + refresh_token?: string; + }; + let refreshToken: string | undefined; + + const { audience, scope } = auth || {}; + + try { + const body = useFormData + ? formDataToObject(fetchOptions.body as string) + : JSON.parse(fetchOptions.body as string); + + if (!body.refresh_token && body.grant_type === 'refresh_token') { + refreshToken = getRefreshToken(audience, scope); + + // When we don't have any refresh_token that matches the audience and scopes + // stored, and useMrrt is configured to true, we will use the last refresh_token + // returned by the server to do a refresh + // We will avoid doing MRRT if we were to downscope while doing refresh in the same audience + 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({ + ...body, + refresh_token: refreshToken + }) + : JSON.stringify({ + ...body, + refresh_token: refreshToken + }); + } + + let abortController: AbortController | undefined; + + if (typeof AbortController === 'function') { + abortController = new AbortController(); + fetchOptions.signal = abortController.signal; + } + + let response: void | Response; + + try { + response = await Promise.race([ + wait(timeout), + fetch(fetchUrl, { ...fetchOptions }) + ]); + } catch (error) { + // fetch error, reject `sendMessage` using `error` key so that we retry. + port.postMessage({ + error: error.message + }); + + return; + } + + if (!response) { + // If the request times out, abort it and let `switchFetch` raise the error. + if (abortController) abortController.abort(); + + port.postMessage({ + error: "Timeout when executing 'fetch'" + }); + + return; + } + + headers = fromEntries(response.headers); + json = await response.json(); + + if (skipTokenStorage) { + delete json.refresh_token; + port.postMessage({ ok: response.ok, json, headers }); + return; + } + + if (json.refresh_token) { + // If useMrrt is configured to true we want to save the latest refresh_token + // to be used when refreshing tokens with MRRT + if (useMrrt) { + refreshTokens["latest_refresh_token"] = json.refresh_token; + + // To avoid having some refresh_token that has already been used + // we will update those inside the list with the new one obtained + // by the server + updateRefreshTokens(refreshToken, json.refresh_token); + } + + setRefreshToken(json.refresh_token, audience, scope); + delete json.refresh_token; + } else if (!preserveRefreshToken) { + // Offline rotating tokens: evict the stored RT so it can't be reused. + // Online (preserveRefreshToken=true) skips this — the ORT is non-rotating + // and the server never returns a replacement. + deleteRefreshToken(audience, scope); + } + + port.postMessage({ + ok: response.ok, + json, + headers + }); + } catch (error) { + port.postMessage({ + ok: false, + json: { + error: error.error, + error_description: error.message + }, + headers + }); + } +}; + +const revokeMessageHandler = async ({ + data: { timeout, auth, fetchUrl, fetchOptions, useFormData }, + ports: [port] +}: MessageEvent) => { + const { audience } = auth || {}; + + try { + const tokensToRevoke = getRefreshTokensByAudience(audience); + + if (tokensToRevoke.length === 0) { + port.postMessage({ ok: true }); + return; + } + + // Parse the base body once; rebuild per RT so each request is independent. + const baseBody = useFormData + ? formDataToObject(fetchOptions.body as string) + : JSON.parse(fetchOptions.body as string); + + for (const refreshToken of tokensToRevoke) { + const body = useFormData + ? createQueryParams({ ...baseBody, token: refreshToken }) + : JSON.stringify({ ...baseBody, token: refreshToken }); + + let abortController: AbortController | undefined; + let signal: AbortSignal | undefined; + + if (typeof AbortController === 'function') { + abortController = new AbortController(); + signal = abortController.signal; + } + + let timeoutId: ReturnType; + let response: void | Response; + + try { + response = await Promise.race([ + new Promise(resolve => { timeoutId = setTimeout(resolve, timeout); }), + fetch(fetchUrl, { ...fetchOptions, body, 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: string | undefined; + try { + const { error_description } = JSON.parse(await response.text()); + errorDescription = error_description; + } catch { + // body absent or not valid JSON + } + + port.postMessage({ error: errorDescription || `HTTP error ${response.status}` }); + return; + } + + deleteRefreshTokensByValue(refreshToken); + } + + port.postMessage({ ok: true }); + } catch (error) { + port.postMessage({ + error: error.message || 'Unknown error during token revocation' + }); + } +}; + +const isAuthorizedWorkerRequest = ( + workerRequest: WorkerRefreshTokenMessage | WorkerRevokeTokenMessage, + expectedPath: string +) => { + 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 { + return false; + } +}; + +const messageRouter = (event: MessageEvent) => { + const { data, ports } = event; + const [port] = ports; + + if ('type' in data && data.type === 'init') { + if (allowedBaseUrl === null) { + try { + new URL(data.allowedBaseUrl); + allowedBaseUrl = data.allowedBaseUrl; + } catch { + return; + } + } + + return; + } + + if ('type' in data && data.type === 'clear') { + refreshTokens = {}; + port?.postMessage({ ok: true }); + return; + } + + if ('type' in data && data.type === 'revoke') { + if (!isAuthorizedWorkerRequest(data as WorkerRevokeTokenMessage, '/oauth/revoke')) { + port?.postMessage({ + ok: false, + json: { + error: 'invalid_fetch_url', + error_description: 'Unauthorized fetch URL' + }, + headers: {} + }); + return; + } + + revokeMessageHandler(event as MessageEvent); + return; + } + + if ( + !('fetchUrl' in data) || + !isAuthorizedWorkerRequest(data as WorkerRefreshTokenMessage, '/oauth/token') + ) { + port?.postMessage({ + ok: false, + json: { + error: 'invalid_fetch_url', + error_description: 'Unauthorized fetch URL' + }, + headers: {} + }); + return; + } + + messageHandler(event as MessageEvent); +}; + +// Don't run `addEventListener` in our tests (this is replaced in rollup) +if (process.env.NODE_ENV === 'test') { + module.exports = { messageHandler, revokeMessageHandler, messageRouter }; + /* c8 ignore next 4 */ +} else { + // @ts-ignore + addEventListener('message', messageRouter); +} diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.types.ts b/backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.types.ts new file mode 100644 index 00000000..163bbd14 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.types.ts @@ -0,0 +1,45 @@ +import { FetchOptions } from '../global'; + +export type WorkerInitMessage = { + type: 'init'; + allowedBaseUrl: string; +}; + +type WorkerTokenMessage = { + timeout: number; + fetchUrl: string; + fetchOptions: FetchOptions; + useFormData?: boolean; + auth: { + audience: string; + scope: string; + }; +}; + +export type WorkerRefreshTokenMessage = WorkerTokenMessage & { + type: 'refresh'; + useMrrt?: boolean; + skipTokenStorage?: boolean; + /** + * When true the worker keeps the stored token if the response carries no replacement + * (Online Refresh Tokens are non-rotating and never come back with a new token). + */ + preserveRefreshToken?: boolean; +}; + +export type WorkerRevokeTokenMessage = Omit & { + type: 'revoke'; + auth: { + audience: string; + }; +}; + +export type WorkerClearMessage = { + type: 'clear'; +}; + +export type WorkerMessage = + | WorkerInitMessage + | WorkerRefreshTokenMessage + | WorkerRevokeTokenMessage + | WorkerClearMessage; diff --git a/backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.utils.ts b/backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.utils.ts new file mode 100644 index 00000000..202c4c66 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-spa-js/src/worker/worker.utils.ts @@ -0,0 +1,38 @@ +import { + WorkerClearMessage, + WorkerRefreshTokenMessage, + WorkerRevokeTokenMessage +} from './worker.types'; + +/** + * Sends a message to a Web Worker and returns a Promise that resolves with + * the worker's response, or rejects if the worker replies with an error. + * + * Uses a {@link MessageChannel} so each call gets its own private reply port, + * making concurrent calls safe without shared state. + * + * @param message - The typed message to send (`refresh` or `revoke`). + * @param to - The target {@link Worker} instance. + * @returns A Promise that resolves with the worker's response payload. + */ +export const sendMessage = ( + message: + | WorkerRefreshTokenMessage + | WorkerRevokeTokenMessage + | WorkerClearMessage, + to: Worker +): Promise => + new Promise(function (resolve, reject) { + const messageChannel = new MessageChannel(); + + messageChannel.port1.onmessage = function (event) { + if (event.data.error) { + reject(new Error(event.data.error)); + } else { + resolve(event.data); + } + messageChannel.port1.close(); + }; + + to.postMessage(message, [messageChannel.port2]); + }); diff --git a/backend/node_modules/@auth0/auth0-vue/LICENSE b/backend/node_modules/@auth0/auth0-vue/LICENSE new file mode 100644 index 00000000..289a362c --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/LICENSE @@ -0,0 +1,1355 @@ +The MIT License (MIT) + +Copyright 2022 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +-------------------------------------------------------------------------------- + + + + +The following 3rd-party software packages may be used by or distributed with git@github.com:auth0/auth0-vue. Any information relevant to third-party vendors listed below are collected using common, reasonable means. + + + +================================================================================ + + + + + Dependencies + + + + +================================================================================ +- @auth0/auth0-spa-js (1.19.4) [MIT] +- @babel/parser (7.16.12) [MIT] +- @vue/compiler-core (3.2.29) [MIT] +- @vue/compiler-dom (3.2.29) [MIT] +- @vue/compiler-sfc (3.2.29) [MIT, Multi-license: BSD-3-Clause OR MIT] +- @vue/compiler-ssr (3.2.29) [MIT] +- @vue/reactivity (3.2.29) [MIT] +- @vue/reactivity-transform (3.2.29) [MIT] +- @vue/runtime-core (3.2.29) [MIT] +- @vue/runtime-dom (3.2.29) [MIT] +- @vue/server-renderer (3.2.29) [MIT] +- @vue/shared (3.2.29) [MIT] +- abortcontroller-polyfill (1.7.3) [MIT] +- browser-tabs-lock (1.2.15) [MIT] +- core-js (3.20.3) [MIT] +- csstype (2.6.19) [MIT] +- es-cookie (1.3.2) [MIT] +- estree-walker (2.0.2) [MIT] +- fast-text-encoding (1.0.3) [Apache-2.0] +- lodash (4.17.21) [MIT, CC0-1.0] +- magic-string (0.25.7) [MIT] +- nanoid (3.1.30) [MIT] +- picocolors (1.0.0) [ISC, MIT] +- postcss (8.4.5) [MIT] +- promise-polyfill (8.2.1) [MIT] +- source-map (0.6.1) [BSD-3-Clause] +- source-map-js (1.0.1) [BSD-3-Clause, Multi-license: BSD-2-Clause OR BSD-3-Clause] +- sourcemap-codec (1.4.8) [MIT] +- unfetch (4.2.0) [MIT] +- vue (3.2.29) [MIT] + + + + +-------------------------------------------------------------------------------- +@auth0/auth0-spa-js (1.19.4) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018 Auth0, Inc. (http://auth0.com) + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + +-------------------------------------------------------------------------------- +@babel/parser (7.16.12) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright (C) 2012-2014 by various contributors (see AUTHORS) + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/compiler-core (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/compiler-dom (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/compiler-sfc (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +* Other Licenses * +Multi-license: BSD-3-Clause OR MIT + + + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/compiler-ssr (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/reactivity (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/reactivity-transform (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright (c) 2022, @vue/reactivity-transform Contributors +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + +-------------------------------------------------------------------------------- +@vue/runtime-core (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/runtime-dom (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/server-renderer (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +@vue/shared (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +abortcontroller-polyfill (1.7.3) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +MIT License + + +Copyright (c) 2017 molsson + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +browser-tabs-lock (1.2.15) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2019 SuperTokens, VRAI Labs Pvt. Ltd, India + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +-------------------------------------------------------------------------------- +core-js (3.20.3) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright (c) 2014-2022 Denis Pushkarev + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +csstype (2.6.19) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright (c) 2017-2018 Fredrik Nicol + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +es-cookie (1.3.2) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright 2017 Theodore Brown + + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +estree-walker (2.0.2) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright (c) 2015-20 [these people](https://github.com/Rich-Harris/estree-walker/graphs/contributors) + + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + +-------------------------------------------------------------------------------- +fast-text-encoding (1.0.3) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +Apache-2.0 + + +Copyright 2017 Sam Thorogood. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with 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. + + + + +-------------------------------------------------------------------------------- +lodash (4.17.21) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +No licenses found + + +* Other Licenses * +MIT, CC0-1.0 + + + + +Copyright OpenJS Foundation and other contributors + + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + + +The following license applies to all parts of this software except as +documented below: + + +==== + + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +==== + + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + + +==== + + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + + + + + + +CC0 1.0 Universal<> +Statement of Purpose +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + 4. Limitations and Disclaimers. + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. + + + + +-------------------------------------------------------------------------------- +magic-string (0.25.7) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright 2018 Rich Harris + + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +nanoid (3.1.30) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright 2017 Andrey Sitnik + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +picocolors (1.0.0) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +ISC, MIT + + +Copyright (c) 2022, picocolors Contributors +Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + + +ISC License + + +Copyright (c) 2021 Alexey Raspopov, Kostiantyn Denysov, Anton Verinov + + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +postcss (8.4.5) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright 2013 Andrey Sitnik + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +promise-polyfill (8.2.1) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +Copyright (c) 2014 Taylor Hakes +Copyright (c) 2014 Forbes Lindesay + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +-------------------------------------------------------------------------------- +source-map (0.6.1) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +BSD-3-Clause + + + + +Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + +* Neither the names of the Mozilla Foundation nor the names of project + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + + + +-------------------------------------------------------------------------------- +source-map-js (1.0.1) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +BSD-3-Clause, Multi-license: BSD-2-Clause OR BSD-3-Clause + + + + +Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + +* Neither the names of the Mozilla Foundation nor the names of project + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + + + + + +Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + +* Neither the names of the Mozilla Foundation nor the names of project + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + + + +-------------------------------------------------------------------------------- +sourcemap-codec (1.4.8) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License + + +Copyright (c) 2015 Rich Harris + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +unfetch (4.2.0) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2017 Jason Miller + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + + + +-------------------------------------------------------------------------------- +vue (3.2.29) +-------------------------------------------------------------------------------- + + +* Declared Licenses * +MIT + + +The MIT License (MIT) + + +Copyright (c) 2018-present, Yuxi (Evan) You + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +—---------------------------------------------------------------------------------------------------------------------------- + + + + +Report Generated by FOSSA on 2022-02-03 diff --git a/backend/node_modules/@auth0/auth0-vue/README.md b/backend/node_modules/@auth0/auth0-vue/README.md new file mode 100644 index 00000000..124c4c92 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/README.md @@ -0,0 +1,181 @@ +![Auth0 SDK for Vue 3 Single Page Applications](https://cdn.auth0.com/website/sdks/banners/auth0-vue-banner.png) + +![Release](https://img.shields.io/npm/v/@auth0/auth0-vue) +[![Codecov](https://codecov.io/gh/auth0/auth0-vue/branch/main/graph/badge.svg)](https://codecov.io/gh/auth0/auth0-vue) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/auth0/auth0-vue) +![Downloads](https://img.shields.io/npm/dw/@auth0/auth0-vue) +[![License](https://img.shields.io/:license-mit-blue.svg?style=flat)](https://opensource.org/licenses/MIT) +[![CircleCI](https://img.shields.io/circleci/build/github/auth0/auth0-vue)](https://circleci.com/gh/auth0/auth0-vue) + +📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started) - 💻 [API Reference](#api-reference) - 💬 [Feedback](#feedback) + +## Documentation + +- [Quickstart](https://auth0.com/docs/quickstart/spa/vuejs) - our interactive guide for quickly adding login, logout and user information to a Vue 3 app using Auth0. +- [Sample App](https://github.com/auth0-samples/auth0-vue-samples/tree/master/01-Login) - a full-fledged Vue 3 application integrated with Auth0. +- [FAQs](https://github.com/auth0/auth0-vue/tree/main/FAQ.md) - frequently asked questions about the auth0-vue SDK. +- [Examples](https://github.com/auth0/auth0-vue/tree/main/EXAMPLES.md) - code samples for common Vue 3 authentication scenario's. +- [Docs site](https://www.auth0.com/docs) - explore our docs site and learn more about Auth0. + +## Getting started + +### Requirements + +This library supports **Vue 3** applications. +For integrating Auth0 with a Vue 2 application, please read [the Vue 2 Tutorial](https://github.com/auth0/auth0-vue/blob/main/tutorial/vue2-login.md). + +### Installation + +Using [npm](https://npmjs.org): + +```sh +npm install @auth0/auth0-vue +``` + +Using [yarn](https://yarnpkg.com): + +```sh +yarn add @auth0/auth0-vue +``` + +### Configure Auth0 + +Create a **Single Page Application** in the [Auth0 Dashboard](https://manage.auth0.com/#/applications). + +> **If you're using an existing application**, verify that you have configured the following settings in your Single Page Application: +> +> - Click on the "Settings" tab of your application's page. +> - Scroll down and click on the "Show Advanced Settings" link. +> - Under "Advanced Settings", click on the "OAuth" tab. +> - Ensure that "JsonWebToken Signature Algorithm" is set to `RS256` and that "OIDC Conformant" is enabled. + +Next, configure the following URLs for your application under the "Application URIs" section of the "Settings" page: + +- **Allowed Callback URLs**: `http://localhost:3000` +- **Allowed Logout URLs**: `http://localhost:3000` +- **Allowed Web Origins**: `http://localhost:3000` + +> These URLs should reflect the origins that your application is running on. **Allowed Callback URLs** may also include a path, depending on where you're handling the callback (see below). + +Take note of the **Client ID** and **Domain** values under the "Basic Information" section. You'll need these values in the next step. + +### Configure the SDK + +Create an instance of the `Auth0Plugin` by calling `createAuth0` and pass it to Vue's `app.use()`. + +```js +import { createAuth0 } from '@auth0/auth0-vue'; + +const app = createApp(App); + +app.use( + createAuth0({ + domain: '', + clientId: '', + authorizationParams: { + redirect_uri: '' + } + }) +); + +app.mount('#app'); +``` + +### Add login to your application + +In order to add login to your application you can use the `loginWithRedirect` function that is exposed on the return value of `useAuth0`, which you can access in your component's `setup` function. + +```html + +``` + +Once setup returns the correct method, you can call that method from your component's HTML. + +```html + +``` + +
+ Using Options API + +```html + + + +``` + +
+ +For more code samples on how to integrate the **auth0-vue** SDK in your **Vue 3** application, have a look at our [examples](https://github.com/auth0/auth0-vue/tree/main/EXAMPLES.md). + +## API reference + +Explore public API's available in auth0-vue. + +- [createAuth0](https://auth0.github.io/auth0-vue/functions/createAuth0.html) +- [createAuthGuard](https://auth0.github.io/auth0-vue/functions/createAuthGuard.html) +- [useAuth0](https://auth0.github.io/auth0-vue/functions/useAuth0.html) +- [Auth0PluginOptions](https://auth0.github.io/auth0-vue/interfaces/Auth0PluginOptions.html) +- [Auth0VueClientOptions](https://auth0.github.io/auth0-vue/interfaces/Auth0VueClientOptions.html) +- [Auth0VueClient](https://auth0.github.io/auth0-vue/interfaces/Auth0VueClient.html) + +## Feedback + +### Contributing + +We appreciate feedback and contribution to this repo! Before you get started, please see the following: + +- [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) +- [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md) +- [This repo's contribution guide](https://github.com/auth0/auth0-vue/blob/main/CONTRIBUTING.md) + +### Raise an issue + +To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-vue/issues). + +### Vulnerability Reporting + +Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/responsible-disclosure-policy) details the procedure for disclosing security issues. + +--- + +

+ + + + Auth0 Logo + +

+

Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0?

+

+This project is licensed under the MIT license. See the LICENSE file for more info.

\ No newline at end of file diff --git a/backend/node_modules/@auth0/auth0-vue/package.json b/backend/node_modules/@auth0/auth0-vue/package.json new file mode 100644 index 00000000..50a85a5d --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/package.json @@ -0,0 +1,139 @@ +{ + "author": "Auth0", + "name": "@auth0/auth0-vue", + "description": "Auth0 SDK for Vue Applications using Authorization Code Grant Flow with PKCE", + "license": "MIT", + "version": "2.9.0", + "type": "module", + "main": "dist/lib/auth0-vue.cjs", + "types": "dist/typings/index.d.ts", + "module": "dist/auth0-vue.production.esm.js", + "exports": { + ".": { + "require": "./dist/lib/auth0-vue.cjs", + "import": "./dist/auth0-vue.production.esm.js", + "types": "./dist/typings/index.d.ts" + } + }, + "scripts": { + "dev": "rimraf dist && rollup -c --watch", + "start": "npm run dev", + "docs": "typedoc --options ./typedoc.js src", + "build": "rimraf dist && rollup -m -c --environment NODE_ENV:production && npm run test:es-check", + "build:stats": "npm run build -- --environment WITH_STATS:true && open stats.html", + "lint": "eslint ./src --ext ts", + "lint:security": "eslint ./src --ext ts --no-eslintrc --config ./.eslintrc.security", + "test": "jest --coverage --silent", + "test:watch": "jest --coverage --watch", + "test:debug": "node --inspect node_modules/.bin/jest --runInBand", + "test:open:integration": "cypress open", + "test:watch:integration": "concurrently --raw npm:dev 'npm:test:open:integration'", + "test:es-check": "npm run test:es-check:es2017 && npm run test:es-check:es2017:module", + "test:es-check:es2017": "es-check es2017 dist/auth0-vue.production.js", + "test:es-check:es2017:module": "es-check es2017 dist/auth0-vue.production.esm.js --module ", + "test:integration:server": "npm run dev", + "test:integration:tests": "wait-on http://localhost:3000/ && cypress run", + "test:integration": "concurrently --raw --kill-others --success first npm:test:integration:server npm:test:integration:tests", + "serve:coverage": "serve coverage/lcov-report -n", + "serve:stats": "serve bundle-stats -n", + "print-bundle-size": "node ./scripts/print-bundle-size.cjs", + "prepack": "npm run build", + "publish:cdn": "ccu --trace" + }, + "devDependencies": { + "@auth0/component-cdn-uploader": "github:auth0/component-cdn-uploader#v3.0.2", + "@jest/globals": "^29.3.1", + "@rollup/plugin-replace": "^3.0.0", + "@types/node": "^25.0.1", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", + "cli-table": "^0.3.11", + "concurrently": "^7.5.0", + "cypress": "^15.18.1", + "es-check": "^7.0.1", + "eslint": "^8.5.0", + "eslint-plugin-security": "^1.5.0", + "gzip-size": "^6.0.0", + "husky": "^7.0.4", + "jest": "^29.3.1", + "jest-environment-jsdom": "^29.3.1", + "jest-junit": "^17.0.0", + "oidc-provider": "^9.6.0", + "prettier": "^2.5.1", + "pretty-quick": "^3.1.3", + "qss": "^2.0.3", + "rimraf": "^3.0.2", + "rollup": "^2.61.1", + "rollup-plugin-analyzer": "^4.0.0", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-dev": "^1.1.3", + "rollup-plugin-livereload": "^2.0.5", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-sourcemaps": "^0.6.3", + "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-typescript2": "^0.37.0", + "rollup-plugin-visualizer": "^5.5.2", + "rollup-plugin-vue": "^6.0.0", + "rollup-plugin-web-worker-loader": "^1.6.1", + "serve": "^14.1.1", + "ts-jest": "^29.0.3", + "tslib": "^2.3.1", + "typedoc": "^0.28.16", + "typescript": "^5.6.3", + "vue-router": "^5.0.2", + "wait-on": "^9.0.10" + }, + "dependencies": { + "@auth0/auth0-spa-js": "^2.23.0", + "vue": "^3.5.21" + }, + "peerDependencies": { + "vue-router": "^4.0.12 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vue-router": { + "optional": true + } + }, + "files": [ + "src", + "dist" + ], + "repository": { + "type": "git", + "url": "git://github.com/auth0/auth0-vue.git" + }, + "bugs": { + "url": "https://github.com/auth0/auth0-vue/issues" + }, + "homepage": "https://github.com/auth0/auth0-vue#readme", + "keywords": [ + "auth0", + "login", + "Authorization Code Grant Flow", + "PKCE", + "Single Page Application authentication", + "SPA authentication", + "Vue" + ], + "ccu": { + "name": "auth0-vue", + "cdn": "https://cdn.auth0.com", + "mainBundleFile": "auth0-vue.production.js", + "bucket": "assets.us.auth0.com", + "localPath": "dist", + "digest": { + "hashes": [ + "sha384" + ], + "extensions": [ + ".js" + ] + } + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + } +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/global.ts b/backend/node_modules/@auth0/auth0-vue/src/global.ts new file mode 100644 index 00000000..b7debb0a --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/global.ts @@ -0,0 +1,74 @@ +export { Auth0Plugin } from './plugin'; +export * from './interfaces'; +export * from './guard'; + +export { + User, + InMemoryCache, + LocalStorageCache, + UseDpopNonceError, + MfaRequiredError, + MfaError, + MfaListAuthenticatorsError, + MfaEnrollmentError, + MfaChallengeError, + MfaVerifyError, + MfaEnrollmentFactorsError, + PasskeyError, + PasskeyRegisterError, + PasskeyChallengeError, + PasskeyGetTokenError, + MyAccountApiError, + MissingScopesError, + InvalidConfigurationError, + RefreshTokenMode +} from '@auth0/auth0-spa-js'; + +export type { + AuthorizationParams, + InteractiveErrorHandler, + CustomTokenExchangeOptions, + TokenEndpointResponse, + PopupLoginOptions, + PopupConfigOptions, + GetTokenWithPopupOptions, + LogoutUrlOptions, + CacheLocation, + GetTokenSilentlyOptions, + IdToken, + ICache, + Cacheable, + FetcherConfig, + Fetcher, + CustomFetchMinimalOutput, + MfaApiClient, + Authenticator, + AuthenticatorType, + OobChannel, + MfaFactorType, + EnrollParams, + EnrollOtpParams, + EnrollSmsParams, + EnrollVoiceParams, + EnrollEmailParams, + EnrollPushParams, + EnrollmentResponse, + OtpEnrollmentResponse, + OobEnrollmentResponse, + ChallengeAuthenticatorParams, + ChallengeResponse, + VerifyParams, + MfaGrantType, + EnrollmentFactor, + PasskeyApiClient, + PasskeySignupOptions, + PasskeyLoginOptions, + MyAccountApiClient, + AuthenticationMethod, + AuthenticationMethodType, + Factor, + UpdateAuthenticationMethodRequest, + EnrollmentChallengeOptions, + EnrollmentChallengeResponse, + EnrollmentVerifyOptions +} from '@auth0/auth0-spa-js'; diff --git a/backend/node_modules/@auth0/auth0-vue/src/guard.ts b/backend/node_modules/@auth0/auth0-vue/src/guard.ts new file mode 100644 index 00000000..d4b0d923 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/guard.ts @@ -0,0 +1,95 @@ +import type { RouteLocation } from 'vue-router'; +import { watchEffectOnceAsync } from './utils'; +import { client as auth0Client } from './plugin'; +import { AUTH0_TOKEN } from './token'; +import type { Auth0VueClient } from './interfaces'; +import type { App } from 'vue'; +import { unref } from 'vue'; +import type { RedirectLoginOptions } from '@auth0/auth0-spa-js'; + +async function createGuardHandler( + client: Auth0VueClient, + to: RouteLocation, + redirectLoginOptions?: RedirectLoginOptions +) { + const fn = async () => { + if (unref(client.isAuthenticated)) { + return true; + } + + await client.loginWithRedirect({ + ...redirectLoginOptions, + appState: { + target: to.fullPath, + ...redirectLoginOptions?.appState + } + }); + + return false; + }; + + if (!unref(client.isLoading)) { + return fn(); + } + + await watchEffectOnceAsync(() => !unref(client.isLoading)); + + return fn(); +} + +/** + * The options used when creating an AuthGuard. + */ +export interface AuthGuardOptions { + /** + * The vue application + */ + app?: App; + + /** + * Route specific options to use when being redirected to Auth0. + * If appState is provided, it will be merged with the guard's automatic + * target property. Any target provided in appState will take precedence over the guard's default target. + */ + redirectLoginOptions?: RedirectLoginOptions; +} + +/** + * + * @param [app] The vue application + */ +export function createAuthGuard( + app?: App +): (to: RouteLocation) => Promise; + +/** + * + * @param [options] The options used when creating an AuthGuard. + */ +export function createAuthGuard( + options?: AuthGuardOptions +): (to: RouteLocation) => Promise; + +export function createAuthGuard( + appOrOptions?: App | AuthGuardOptions +): (to: RouteLocation) => Promise { + const { app, redirectLoginOptions } = + !appOrOptions || 'config' in appOrOptions + ? { app: appOrOptions as App, redirectLoginOptions: undefined } + : (appOrOptions as AuthGuardOptions); + + return async (to: RouteLocation) => { + // eslint-disable-next-line security/detect-object-injection + const auth0 = app + ? (app.config.globalProperties[AUTH0_TOKEN] as Auth0VueClient) + : (unref(auth0Client) as Auth0VueClient); + + return createGuardHandler(auth0, to, redirectLoginOptions); + }; +} + +export async function authGuard(to: RouteLocation) { + const auth0 = unref(auth0Client) as Auth0VueClient; + + return createGuardHandler(auth0, to); +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/index.cjs.ts b/backend/node_modules/@auth0/auth0-vue/src/index.cjs.ts new file mode 100644 index 00000000..ebc19a16 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/index.cjs.ts @@ -0,0 +1,3 @@ +import * as Auth0Vue from './index'; + +export default Auth0Vue; diff --git a/backend/node_modules/@auth0/auth0-vue/src/index.ts b/backend/node_modules/@auth0/auth0-vue/src/index.ts new file mode 100644 index 00000000..818e7640 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/index.ts @@ -0,0 +1,42 @@ +import { inject } from 'vue'; +import './global'; +import { Auth0Plugin } from './global'; +import type { + Auth0VueClient, + Auth0PluginOptions, + Auth0VueClientOptions +} from './global'; +import { AUTH0_INJECTION_KEY, AUTH0_TOKEN } from './token'; +import { deprecateRedirectUri } from './utils'; + +export * from './global'; +export { AUTH0_INJECTION_KEY } from './token'; + +declare module 'vue' { + export interface ComponentCustomProperties { + [AUTH0_TOKEN]: Auth0VueClient; + } +} + +/** + * Creates the Auth0 plugin. + * + * @param clientOptions The Auth Vue Client Options + * @param pluginOptions Additional Plugin Configuration Options + * @returns An instance of Auth0Plugin + */ +export function createAuth0( + clientOptions: Auth0VueClientOptions, + pluginOptions?: Auth0PluginOptions +) { + deprecateRedirectUri(clientOptions); + return new Auth0Plugin(clientOptions, pluginOptions); +} + +/** + * Returns the registered Auth0 instance using Vue's `inject`. + * @returns An instance of Auth0VueClient + */ +export function useAuth0(): Auth0VueClient { + return inject(AUTH0_INJECTION_KEY) as Auth0VueClient; +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/interfaces/app-state.ts b/backend/node_modules/@auth0/auth0-vue/src/interfaces/app-state.ts new file mode 100644 index 00000000..929cb70a --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/interfaces/app-state.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +export interface AppState { + /** + * Target path the app gets routed to after + * handling the callback from Auth0 (defaults to '/') + */ + target?: string; + + /** + * Any custom parameter to be stored in appState + */ + [key: string]: any; +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-plugin-options.ts b/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-plugin-options.ts new file mode 100644 index 00000000..dab3c77c --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-plugin-options.ts @@ -0,0 +1,30 @@ +/** + * Additional Configuration for the Auth0 Vue plugin + */ +export interface Auth0PluginOptions { + /** + * By default, if the page URL has code and state parameters, the SDK will assume it should handle it and attempt to exchange the code for a token. + * + * In situations where you are combining our SDK with other libraries that use the same `code` and `state` parameters, + * you will need to ensure our SDK can differentiate between requests it should and should not handle. + * + * In these cases you can instruct the client to ignore certain URLs by setting `skipRedirectCallback`. + * + * ```js + * createAuth0({}, { + * skipRedirectCallback: window.location.pathname === '/other-callback' + * }) + * ``` + * + * **Note**: In the above example, `/other-callback` is an existing route, with a `code` (or `error` in case when something went wrong) and `state`, that will be handled + * by any other SDK. + * + */ + skipRedirectCallback?: boolean; + + /** + * Path in your application to redirect to when the Authorization server + * returns an error. Defaults to `/` + */ + errorPath?: string; +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client-options.ts b/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client-options.ts new file mode 100644 index 00000000..99b1adb7 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client-options.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +import type { + Auth0ClientOptions, + LogoutOptions as SPALogoutOptions, + RedirectLoginOptions as SPARedirectLoginOptions +} from '@auth0/auth0-spa-js'; +import type { AppState } from './app-state'; + +/** + * Configuration for the Auth0 Vue Client + */ +export interface Auth0VueClientOptions extends Auth0ClientOptions {} + +export interface LogoutOptions extends Omit {} +export interface RedirectLoginOptions + extends Omit, 'onRedirect'> {} diff --git a/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client.ts b/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client.ts new file mode 100644 index 00000000..491db88b --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/interfaces/auth0-vue-client.ts @@ -0,0 +1,410 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { + User, + IdToken, + PopupLoginOptions, + PopupConfigOptions, + RedirectLoginResult, + GetTokenSilentlyOptions, + GetTokenSilentlyVerboseResponse, + GetTokenWithPopupOptions, + ConnectAccountRedirectResult, + CustomFetchMinimalOutput, + Fetcher, + FetcherConfig, + MfaApiClient, + CustomTokenExchangeOptions, + TokenEndpointResponse, + PasskeyApiClient, + MyAccountApiClient +} from '@auth0/auth0-spa-js'; +import type { Ref } from 'vue'; +import type { AppState } from './app-state'; +import type { + LogoutOptions, + RedirectLoginOptions +} from './auth0-vue-client-options'; + +export interface Auth0VueClient { + /** + * The loading state of the SDK, `true` if the SDK is still processing the PKCE flow, `false` if the SDK has finished processing the PKCE flow. + */ + isLoading: Ref; + + /** + * The authentication state, `true` if the user is authenticated, `false` if not. + */ + isAuthenticated: Ref; + + /** + * Contains the information of the user if available. + */ + user: Ref; + + /** + * Contains all claims from the id_token if available. + */ + idTokenClaims: Ref; + + /** + * Contains an error that occured in the SDK + */ + error: Ref; + + /** + * ```js + * try { + * await loginWithPopup(options); + * } catch(e) { + * if (e instanceof PopupCancelledError) { + * // Popup was closed before login completed + * } + * } + * ``` + * + * Opens a popup with the `/authorize` URL using the parameters + * provided as arguments. Random and secure `state` and `nonce` + * parameters will be auto-generated. If the response is successful, + * results will be valid according to their expiration times. + * + * IMPORTANT: This method has to be called from an event handler + * that was started by the user like a button click, for example, + * otherwise the popup will be blocked in most browsers. + * + * @param options + * @param config + */ + loginWithPopup( + options?: PopupLoginOptions, + config?: PopupConfigOptions + ): Promise; + + /** + * ```js + * await loginWithRedirect(options); + * ``` + * + * Performs a redirect to `/authorize` using the parameters + * provided as arguments. Random and secure `state` and `nonce` + * parameters will be auto-generated. + * + * @param options + */ + loginWithRedirect(options?: RedirectLoginOptions): Promise; + + /** + * ```js + * const tokenResponse = await loginWithCustomTokenExchange({ + * subject_token: 'external_token_value', + * subject_token_type: 'urn:acme:legacy-system-token', + * scope: 'openid profile email' + * }); + * ``` + * + * Exchanges an external subject token for Auth0 tokens and logs the user in. + * This method implements the Custom Token Exchange grant as specified in RFC 8693. + * + * The exchanged tokens are automatically cached, establishing an authenticated session. + * After calling this method, `isAuthenticated` will be `true` and `user` will contain + * the user's information. + * + * @param options - The options required to perform the token exchange + * @returns A promise that resolves to the token endpoint response containing Auth0 tokens + */ + loginWithCustomTokenExchange( + options: CustomTokenExchangeOptions + ): Promise; + + /** + * After the browser redirects back to the callback page, + * call `handleRedirectCallback` to handle success and error + * responses from Auth0. If the response is successful, results + * will be valid according to their expiration times. + * + * **Note:** The Auth0-Vue SDK handles this for you, unless you set `skipRedirectCallback` to true. + * In that case, be sure to explicitly call `handleRedirectCallback` yourself. + */ + handleRedirectCallback( + url?: string + ): Promise< + RedirectLoginResult | ConnectAccountRedirectResult + >; + + /** + * ```js + * await checkSession(); + * ``` + * + * Check if the user is logged in using `getTokenSilently`. The difference + * with `getTokenSilently` is that this doesn't return a token, but it will + * pre-fill the token cache. + * + * This method also heeds the `auth0.{clientId}.is.authenticated` cookie, as an optimization + * to prevent calling Auth0 unnecessarily. If the cookie is not present because + * there was no previous login (or it has expired) then tokens will not be refreshed. + * + * @param options + */ + checkSession(options?: GetTokenSilentlyOptions): Promise; + + /** + * Fetches a new access token and returns the response from the /oauth/token endpoint, omitting the refresh token. + * + * @param options + */ + getAccessTokenSilently( + options: GetTokenSilentlyOptions & { detailedResponse: true } + ): Promise; + + /** + * Fetches a new access token and returns it. + * + * @param options + */ + getAccessTokenSilently(options?: GetTokenSilentlyOptions): Promise; + + /** + * ```js + * const token = await getTokenWithPopup(options); + * ``` + * Opens a popup with the `/authorize` URL using the parameters + * provided as arguments. Random and secure `state` and `nonce` + * parameters will be auto-generated. If the response is successful, + * results will be valid according to their expiration times. + * + * @param options + * @param config + */ + getAccessTokenWithPopup( + options?: GetTokenWithPopupOptions, + config?: PopupConfigOptions + ): Promise; + + /** + * ```js + * logout(); + * ``` + * + * Clears the application session and performs a redirect to `/v2/logout`, using + * the parameters provided as arguments, to clear the Auth0 session. + * + * **Note:** If you are using a custom cache, and specifying `localOnly: true`, and you want to perform actions or read state from the SDK immediately after logout, you should `await` the result of calling `logout`. + * + * If the `federated` option is specified it also clears the Identity Provider session. + * If the `localOnly` option is specified, it only clears the application session. + * It is invalid to set both the `federated` and `localOnly` options to `true`, + * and an error will be thrown if you do. + * [Read more about how Logout works at Auth0](https://auth0.com/docs/logout). + * + * @param options + */ + logout(options?: LogoutOptions): Promise; + + /** + * ```js + * const nonce = await getDpopNonce(); + * ``` + * + * Retrieves the current DPoP nonce value for a specific identifier. + * + * The nonce is used to prevent replay attacks when using DPoP (Demonstrating Proof-of-Possession). + * It may return `undefined` initially before the first server response. + * + * **Note:** Requires `useDpop: true` in the Auth0 client configuration. + * + * @param id - Optional identifier for the nonce. If omitted, returns the nonce for Auth0 requests. + * Use a custom identifier for tracking nonces for different API endpoints. + */ + getDpopNonce(id?: string): Promise; + + /** + * ```js + * await setDpopNonce('new-nonce-value', 'my-api'); + * ``` + * + * Stores a DPoP nonce value for future use with a specific identifier. + * + * This is typically called automatically when the server provides a new nonce + * in the `DPoP-Nonce` response header. Manual usage is only needed for advanced scenarios. + * + * **Note:** Requires `useDpop: true` in the Auth0 client configuration. + * + * @param nonce - The nonce value to store + * @param id - Optional identifier for the nonce. If omitted, sets the nonce for Auth0 requests. + * Use a custom identifier for managing nonces for different API endpoints. + */ + setDpopNonce(nonce: string, id?: string): Promise; + + /** + * ```js + * const proof = await generateDpopProof({ + * url: 'https://api.example.com/data', + * method: 'GET', + * accessToken: token + * }); + * ``` + * + * Generates a DPoP proof JWT that cryptographically binds an access token to the current client. + * + * The proof is a signed JWT that demonstrates possession of the private key associated with + * the public key in the access token. This prevents token theft and replay attacks. + * + * **Note:** Requires `useDpop: true` in the Auth0 client configuration. + * Most developers should use `createFetcher()` instead, which handles proof generation automatically. + * + * @param params - Configuration for generating the proof + * @param params.url - The target URL for the API request + * @param params.method - The HTTP method (GET, POST, etc.) + * @param params.accessToken - The access token to bind to the proof + * @param params.nonce - Optional nonce value from a previous server response + */ + generateDpopProof(params: { + url: string; + method: string; + accessToken: string; + nonce?: string; + }): Promise; + + /** + * ```js + * const fetcher = createFetcher({ + * dpopNonceId: 'my-api', + * baseUrl: 'https://api.example.com' + * }); + * + * const response = await fetcher.fetchWithAuth('/data', { + * method: 'GET' + * }); + * const data = await response.json(); + * ``` + * + * Creates a fetcher instance that automatically handles authentication for API requests. + * + * The fetcher automatically: + * - Retrieves access tokens using `getAccessTokenSilently()` + * - Adds proper `Authorization` headers + * - Generates and includes DPoP proofs when using DPoP tokens + * - Manages DPoP nonces and retries on nonce errors + * - Handles token refreshing + * + * This is the recommended way to make authenticated API calls, especially when using DPoP. + * + * @param config - Configuration options for the fetcher + */ + createFetcher( + config?: FetcherConfig + ): Fetcher; + + /** + * MFA API client for multi-factor authentication operations. + * + * Use this property to handle MFA challenge and enrollment flows + * after receiving a `MfaRequiredError` from `getAccessTokenSilently`. + * + * **Note:** `mfa.verify()` returns raw tokens but does not automatically + * update Vue's reactive state (`isAuthenticated`, `user`, `idTokenClaims`). + * Call `checkSession()` after a successful `verify()` to reflect the new + * session in your components. + * + * **Note:** Errors thrown by `mfa` methods are **not** captured in the + * `error` ref returned by `useAuth0()`. Unlike other SDK methods, MFA + * operations bypass the plugin's error handling proxy. Always wrap `mfa` + * calls in a `try/catch` and handle typed MFA errors (e.g. + * `MfaVerifyError`, `MfaChallengeError`) directly in your component. + * + * ```js + * import { MfaRequiredError } from '@auth0/auth0-vue'; + * + * const { getAccessTokenSilently, mfa, checkSession } = useAuth0(); + * + * try { + * await getAccessTokenSilently(); + * } catch (e) { + * if (e instanceof MfaRequiredError) { + * if (e.mfa_requirements?.enroll?.length) { + * const factors = await mfa.getEnrollmentFactors(e.mfa_token); + * // ... show enrollment UI + * } else { + * const authenticators = await mfa.getAuthenticators(e.mfa_token); + * // ... show challenge UI, then: + * await mfa.verify({ mfaToken: e.mfa_token, otp: userCode }); + * await checkSession(); // refresh isAuthenticated, user, etc. + * } + * } + * } + * ``` + */ + mfa: MfaApiClient; + + /** + * ```js + * const { passkey } = useAuth0(); + * const tokens = await passkey.signup({ email: 'user@example.com' }); + * ``` + * + * Passkey API client for WebAuthn-based passwordless authentication. + * + * - `signup(options)` — register a new user and create a passkey credential + * - `login(options?)` — authenticate an existing user via passkey assertion + * + * Both methods handle the full WebAuthn flow internally (challenge → browser + * credential ceremony → token exchange) and automatically update + * `isAuthenticated`, `user`, and `idTokenClaims` on completion. + * + * **Note:** Errors thrown by `passkey` methods are captured in the `error` + * ref. You can also wrap calls in a `try/catch` to handle typed passkey + * errors (e.g. `PasskeyError`, `PasskeyRegisterError`) directly in your component. + * + * ```js + * import { PasskeyError } from '@auth0/auth0-vue'; + * + * const { passkey } = useAuth0(); + * + * try { + * await passkey.signup({ email: 'user@example.com' }); + * // isAuthenticated, user, and idTokenClaims are updated automatically + * } catch (e) { + * if (e instanceof PasskeyError) { + * // handle passkey-specific errors + * } + * } + * ``` + */ + passkey: PasskeyApiClient; + + /** + * ```js + * const { myAccount } = useAuth0(); + * const factors = await myAccount.getFactors(); + * ``` + * + * MyAccount API client for self-service account management operations. + * + * Provides methods for managing the authenticated user's authentication + * methods and factors: + * - `getFactors()` — list all enabled factors and those available for enrollment + * - `getAuthenticationMethods(type?)` — list enrolled authentication methods, optionally filtered by type + * - `getAuthenticationMethod(id)` — get a single authentication method by ID + * - `updateAuthenticationMethod(id, data)` — update an authentication method (e.g. rename) + * - `deleteAuthenticationMethod(id)` — remove an enrolled authentication method + * - `enrollmentChallenge(options)` — initiate a two-step enrollment challenge + * - `enrollmentVerify(options)` — complete enrollment by verifying the challenge + * + * **Note:** MyAccount API calls require an access token with the appropriate + * scope (e.g. `read:me:authentication_methods`). Use MRRT to exchange a + * refresh token for a scoped access token if needed. + * + * ```js + * const { myAccount } = useAuth0(); + * + * // List passkey authentication methods + * const methods = await myAccount.getAuthenticationMethods('passkey'); + * + * // Enroll a new passkey + * const challenge = await myAccount.enrollmentChallenge({ type: 'passkey' }); + * const credential = await navigator.credentials.create({ publicKey: challenge.authn_params_public_key }); + * await myAccount.enrollmentVerify({ type: 'passkey', auth_session: challenge.auth_session, location: challenge.location, authn_response: credential }); + * ``` + */ + myAccount: MyAccountApiClient; +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/interfaces/index.ts b/backend/node_modules/@auth0/auth0-vue/src/interfaces/index.ts new file mode 100644 index 00000000..25387479 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/interfaces/index.ts @@ -0,0 +1,4 @@ +export * from './app-state'; +export * from './auth0-plugin-options'; +export * from './auth0-vue-client'; +export * from './auth0-vue-client-options'; diff --git a/backend/node_modules/@auth0/auth0-vue/src/plugin.ts b/backend/node_modules/@auth0/auth0-vue/src/plugin.ts new file mode 100644 index 00000000..ffb70606 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/plugin.ts @@ -0,0 +1,301 @@ +import type { App, Ref } from 'vue'; +import { ref } from 'vue'; +import type { Router } from 'vue-router'; +import type { + AppState, + Auth0PluginOptions, + Auth0VueClient, + Auth0VueClientOptions, + LogoutOptions, + RedirectLoginOptions +} from './interfaces'; +import { AUTH0_INJECTION_KEY, AUTH0_TOKEN } from './token'; +import version from './version'; +import type { + GetTokenSilentlyOptions, + GetTokenSilentlyVerboseResponse, + GetTokenWithPopupOptions, + IdToken, + PopupConfigOptions, + PopupLoginOptions, + RedirectLoginResult, + ConnectAccountRedirectResult, + FetcherConfig, + Fetcher, + CustomFetchMinimalOutput, + MfaApiClient, + CustomTokenExchangeOptions, + TokenEndpointResponse, + PasskeyApiClient, + PasskeySignupOptions, + PasskeyLoginOptions, + MyAccountApiClient +} from '@auth0/auth0-spa-js'; +import { Auth0Client, User } from '@auth0/auth0-spa-js'; +import { bindPluginMethods, deprecateRedirectUri } from './utils'; + +/** + * Helper callback that's used by default before the plugin is installed. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const PLUGIN_NOT_INSTALLED_HANDLER: any = () => { + console.error(`Please ensure Auth0's Vue plugin is correctly installed.`); +}; + +/** + * Helper client that's used by default before the plugin is installed. + */ +const PLUGIN_NOT_INSTALLED_CLIENT: Auth0VueClient = { + isLoading: ref(false), + isAuthenticated: ref(false), + user: ref(undefined), + idTokenClaims: ref(undefined), + error: ref(null), + loginWithPopup: PLUGIN_NOT_INSTALLED_HANDLER, + loginWithRedirect: PLUGIN_NOT_INSTALLED_HANDLER, + loginWithCustomTokenExchange: PLUGIN_NOT_INSTALLED_HANDLER, + getAccessTokenSilently: PLUGIN_NOT_INSTALLED_HANDLER, + getAccessTokenWithPopup: PLUGIN_NOT_INSTALLED_HANDLER, + logout: PLUGIN_NOT_INSTALLED_HANDLER, + checkSession: PLUGIN_NOT_INSTALLED_HANDLER, + handleRedirectCallback: PLUGIN_NOT_INSTALLED_HANDLER, + getDpopNonce: PLUGIN_NOT_INSTALLED_HANDLER, + setDpopNonce: PLUGIN_NOT_INSTALLED_HANDLER, + generateDpopProof: PLUGIN_NOT_INSTALLED_HANDLER, + createFetcher: PLUGIN_NOT_INSTALLED_HANDLER, + mfa: { + setMFAAuthDetails: PLUGIN_NOT_INSTALLED_HANDLER, + getAuthenticators: PLUGIN_NOT_INSTALLED_HANDLER, + enroll: PLUGIN_NOT_INSTALLED_HANDLER, + challenge: PLUGIN_NOT_INSTALLED_HANDLER, + verify: PLUGIN_NOT_INSTALLED_HANDLER, + getEnrollmentFactors: PLUGIN_NOT_INSTALLED_HANDLER + } as unknown as MfaApiClient, + passkey: { + signup: PLUGIN_NOT_INSTALLED_HANDLER, + login: PLUGIN_NOT_INSTALLED_HANDLER + } as unknown as PasskeyApiClient, + myAccount: { + getFactors: PLUGIN_NOT_INSTALLED_HANDLER, + getAuthenticationMethods: PLUGIN_NOT_INSTALLED_HANDLER, + getAuthenticationMethod: PLUGIN_NOT_INSTALLED_HANDLER, + updateAuthenticationMethod: PLUGIN_NOT_INSTALLED_HANDLER, + deleteAuthenticationMethod: PLUGIN_NOT_INSTALLED_HANDLER, + enrollmentChallenge: PLUGIN_NOT_INSTALLED_HANDLER, + enrollmentVerify: PLUGIN_NOT_INSTALLED_HANDLER + } as unknown as MyAccountApiClient +}; + +/** + * @ignore + */ +export const client: Ref = ref( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + PLUGIN_NOT_INSTALLED_CLIENT as any +); + +/** + * @ignore + */ +export class Auth0Plugin implements Auth0VueClient { + private _client!: Auth0Client; + public isLoading: Ref = ref(true); + public isAuthenticated: Ref = ref(false); + public user: Ref = ref({}); + public idTokenClaims = ref(); + public error = ref(null); + public mfa: MfaApiClient = PLUGIN_NOT_INSTALLED_CLIENT.mfa; + public passkey: PasskeyApiClient = PLUGIN_NOT_INSTALLED_CLIENT.passkey; + public myAccount: MyAccountApiClient = PLUGIN_NOT_INSTALLED_CLIENT.myAccount; + + constructor( + private clientOptions: Auth0VueClientOptions, + private pluginOptions?: Auth0PluginOptions + ) { + // Vue Plugins can have issues when passing around the instance to `provide` + // Therefor we need to bind all methods correctly to `this`. + bindPluginMethods(this, ['constructor']); + } + + install(app: App) { + this._client = new Auth0Client({ + ...this.clientOptions, + auth0Client: { + name: 'auth0-vue', + version: version + } + }); + + this.mfa = this._client.mfa; + this.myAccount = this._client.myAccount; + + const passkeyClient = this._client.passkey; + this.passkey = { + signup: (options: PasskeySignupOptions) => this.__proxy(() => passkeyClient.signup(options)), + login: (options?: PasskeyLoginOptions) => this.__proxy(() => passkeyClient.login(options)) + } as unknown as PasskeyApiClient; + + this.__checkSession(app.config.globalProperties.$router); + + // eslint-disable-next-line security/detect-object-injection + app.config.globalProperties[AUTH0_TOKEN] = this; + app.provide(AUTH0_INJECTION_KEY, this as Auth0VueClient); + + client.value = this as Auth0VueClient; + } + + async loginWithRedirect(options?: RedirectLoginOptions) { + deprecateRedirectUri(options); + return this._client.loginWithRedirect(options); + } + + async loginWithPopup( + options?: PopupLoginOptions, + config?: PopupConfigOptions + ) { + deprecateRedirectUri(options); + return this.__proxy(() => this._client.loginWithPopup(options, config)); + } + + async logout(options?: LogoutOptions) { + if (options?.openUrl || options?.openUrl === false) { + return this.__proxy(() => this._client.logout(options)); + } + + return this._client.logout(options); + } + + /* istanbul ignore next */ + async getAccessTokenSilently( + options: GetTokenSilentlyOptions & { detailedResponse: true } + ): Promise; + /* istanbul ignore next */ + async getAccessTokenSilently( + options?: GetTokenSilentlyOptions + ): Promise; + /* istanbul ignore next */ + async getAccessTokenSilently( + options: GetTokenSilentlyOptions = {} + ): Promise { + deprecateRedirectUri(options); + return this.__proxy(() => this._client.getTokenSilently(options)); + } + + async getAccessTokenWithPopup( + options?: GetTokenWithPopupOptions, + config?: PopupConfigOptions + ) { + deprecateRedirectUri(options); + return this.__proxy(() => this._client.getTokenWithPopup(options, config)); + } + + async loginWithCustomTokenExchange( + options: CustomTokenExchangeOptions + ): Promise { + return this.__proxy(() => + this._client.loginWithCustomTokenExchange(options) + ); + } + + async checkSession(options?: GetTokenSilentlyOptions) { + return this.__proxy(() => this._client.checkSession(options)); + } + + async handleRedirectCallback( + url?: string + ): Promise< + RedirectLoginResult | ConnectAccountRedirectResult + > { + return this.__proxy(() => + this._client.handleRedirectCallback(url) + ); + } + + async getDpopNonce(id?: string): Promise { + return this.__proxy(() => this._client.getDpopNonce(id)); + } + + async setDpopNonce(nonce: string, id?: string): Promise { + return this.__proxy(() => this._client.setDpopNonce(nonce, id)); + } + + async generateDpopProof(params: { + url: string; + method: string; + accessToken: string; + nonce?: string; + }): Promise { + return this.__proxy(() => this._client.generateDpopProof(params)); + } + + createFetcher( + config?: FetcherConfig + ): Fetcher { + return this._client.createFetcher(config); + } + + private async __checkSession(router?: Router) { + const search = window.location.search; + + try { + if ( + (search.includes('code=') || search.includes('error=')) && + search.includes('state=') && + !this.pluginOptions?.skipRedirectCallback + ) { + const result = await this.handleRedirectCallback(); + const appState = result?.appState; + const appStateTarget = appState?.target; + const target = appStateTarget ?? '/'; + + if (router) { + window.history.replaceState({}, '', window.location.pathname); + router.push(target); + } else { + window.history.replaceState( + {}, + '', + appStateTarget ?? window.location.pathname + ); + } + + return result; + } else { + await this.checkSession(); + } + } catch (e) { + // __checkSession should never throw an exception as it will fail installing the plugin. + // Instead, errors during __checkSession are propagated using the errors property on `useAuth0`. + + window.history.replaceState({}, '', '/'); + + if (router) { + router.push(this.pluginOptions?.errorPath || '/'); + } + } + } + + private async __refreshState() { + this.isAuthenticated.value = await this._client.isAuthenticated(); + this.user.value = await this._client.getUser(); + this.idTokenClaims.value = await this._client.getIdTokenClaims(); + this.isLoading.value = false; + } + + private async __proxy(cb: () => T, refreshState = true) { + let result; + try { + result = await cb(); + this.error.value = null; + } catch (e) { + this.error.value = e as Error; + throw e; + } finally { + if (refreshState) { + await this.__refreshState(); + } + } + + return result; + } +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/token.ts b/backend/node_modules/@auth0/auth0-vue/src/token.ts new file mode 100644 index 00000000..39cdeb5b --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/token.ts @@ -0,0 +1,17 @@ +import type { InjectionKey } from 'vue'; +import type { Auth0VueClient } from './interfaces'; + +/** + * @ignore + */ +export const AUTH0_TOKEN = '$auth0'; + +/** + * Injection token used to `provide` the `Auth0VueClient` instance. Can be used to pass to `inject()` + * + * ```js + * inject(AUTH0_INJECTION_KEY) + * ``` + */ +export const AUTH0_INJECTION_KEY: InjectionKey = + Symbol(AUTH0_TOKEN); diff --git a/backend/node_modules/@auth0/auth0-vue/src/utils.ts b/backend/node_modules/@auth0/auth0-vue/src/utils.ts new file mode 100644 index 00000000..5ffdef29 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/utils.ts @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/ban-types */ +import { watchEffect } from 'vue'; + +/** + * @ignore + * Run watchEffect untill the watcher returns true, then stop the watch. + * Once it returns true, the promise will resolve. + */ +export function watchEffectOnceAsync(watcher: () => T) { + return new Promise(resolve => { + watchEffectOnce(watcher, resolve); + }); +} + +/** + * @ignore + * Run watchEffect untill the watcher returns true, then stop the watch. + * Once it returns true, it will call the provided function. + */ +export function watchEffectOnce(watcher: () => T, fn: Function) { + const stopWatch = watchEffect(() => { + if (watcher()) { + fn(); + stopWatch(); + } + }); +} + +/** + * @ignore + * Helper function to bind methods to itself, useful when using Vue's `provide` / `inject` API's. + */ +export function bindPluginMethods(plugin: any, exclude: string[]) { + Object.getOwnPropertyNames(Object.getPrototypeOf(plugin)) + .filter(method => !exclude.includes(method)) + // eslint-disable-next-line security/detect-object-injection + .forEach(method => (plugin[method] = plugin[method].bind(plugin))); +} + +/** + * @ignore + * Helper function to map the v1 `redirect_uri` option to the v2 `authorizationParams.redirect_uri` + * and log a warning. + */ +export function deprecateRedirectUri(options?: any) { + if (options?.redirect_uri) { + console.warn( + 'Using `redirect_uri` has been deprecated, please use `authorizationParams.redirect_uri` instead as `redirectUri` will be no longer supported in a future version' + ); + options.authorizationParams = options.authorizationParams || {}; + options.authorizationParams.redirect_uri = options.redirect_uri; + delete options.redirect_uri; + } +} diff --git a/backend/node_modules/@auth0/auth0-vue/src/version.ts b/backend/node_modules/@auth0/auth0-vue/src/version.ts new file mode 100644 index 00000000..77445623 --- /dev/null +++ b/backend/node_modules/@auth0/auth0-vue/src/version.ts @@ -0,0 +1 @@ +export default '2.3.1'; diff --git a/backend/node_modules/@babel/helper-string-parser/LICENSE b/backend/node_modules/@babel/helper-string-parser/LICENSE new file mode 100644 index 00000000..f31575ec --- /dev/null +++ b/backend/node_modules/@babel/helper-string-parser/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/@babel/helper-string-parser/README.md b/backend/node_modules/@babel/helper-string-parser/README.md new file mode 100644 index 00000000..771b4700 --- /dev/null +++ b/backend/node_modules/@babel/helper-string-parser/README.md @@ -0,0 +1,19 @@ +# @babel/helper-string-parser + +> A utility package to parse strings + +See our website [@babel/helper-string-parser](https://babeljs.io/docs/babel-helper-string-parser) for more information. + +## Install + +Using npm: + +```sh +npm install --save @babel/helper-string-parser +``` + +or using yarn: + +```sh +yarn add @babel/helper-string-parser +``` diff --git a/backend/node_modules/@babel/helper-string-parser/package.json b/backend/node_modules/@babel/helper-string-parser/package.json new file mode 100644 index 00000000..7192a148 --- /dev/null +++ b/backend/node_modules/@babel/helper-string-parser/package.json @@ -0,0 +1,31 @@ +{ + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "description": "A utility package to parse strings", + "repository": { + "type": "git", + "url": "https://github.com/babel/babel.git", + "directory": "packages/babel-helper-string-parser" + }, + "homepage": "https://babel.dev/docs/en/next/babel-helper-string-parser", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "./lib/index.js", + "devDependencies": { + "charcodes": "^0.2.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "author": "The Babel Team (https://babel.dev/team)", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./package.json": "./package.json" + }, + "type": "commonjs" +} \ No newline at end of file diff --git a/backend/node_modules/@babel/helper-validator-identifier/LICENSE b/backend/node_modules/@babel/helper-validator-identifier/LICENSE new file mode 100644 index 00000000..f31575ec --- /dev/null +++ b/backend/node_modules/@babel/helper-validator-identifier/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/@babel/helper-validator-identifier/README.md b/backend/node_modules/@babel/helper-validator-identifier/README.md new file mode 100644 index 00000000..05c19e64 --- /dev/null +++ b/backend/node_modules/@babel/helper-validator-identifier/README.md @@ -0,0 +1,19 @@ +# @babel/helper-validator-identifier + +> Validate identifier/keywords name + +See our website [@babel/helper-validator-identifier](https://babeljs.io/docs/babel-helper-validator-identifier) for more information. + +## Install + +Using npm: + +```sh +npm install --save @babel/helper-validator-identifier +``` + +or using yarn: + +```sh +yarn add @babel/helper-validator-identifier +``` diff --git a/backend/node_modules/@babel/helper-validator-identifier/package.json b/backend/node_modules/@babel/helper-validator-identifier/package.json new file mode 100644 index 00000000..9904752a --- /dev/null +++ b/backend/node_modules/@babel/helper-validator-identifier/package.json @@ -0,0 +1,31 @@ +{ + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "description": "Validate identifier/keywords name", + "repository": { + "type": "git", + "url": "https://github.com/babel/babel.git", + "directory": "packages/babel-helper-validator-identifier" + }, + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "./lib/index.js", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./package.json": "./package.json" + }, + "devDependencies": { + "@unicode/unicode-17.0.0": "^1.6.10", + "charcodes": "^0.2.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "author": "The Babel Team (https://babel.dev/team)", + "type": "commonjs" +} \ No newline at end of file diff --git a/backend/node_modules/@babel/parser/CHANGELOG.md b/backend/node_modules/@babel/parser/CHANGELOG.md new file mode 100644 index 00000000..b3840ac8 --- /dev/null +++ b/backend/node_modules/@babel/parser/CHANGELOG.md @@ -0,0 +1,1073 @@ +# Changelog + +> **Tags:** +> - :boom: [Breaking Change] +> - :eyeglasses: [Spec Compliance] +> - :rocket: [New Feature] +> - :bug: [Bug Fix] +> - :memo: [Documentation] +> - :house: [Internal] +> - :nail_care: [Polish] + +> Semver Policy: https://github.com/babel/babel/tree/main/packages/babel-parser#semver + +_Note: Gaps between patch versions are faulty, broken or test releases._ + +See the [Babel Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) for the pre-6.8.0 version Changelog. + +## 6.17.1 (2017-05-10) + +### :bug: Bug Fix + * Fix typo in flow spread operator error (Brian Ng) + * Fixed invalid number literal parsing ([#473](https://github.com/babel/babylon/pull/473)) (Alex Kuzmenko) + * Fix number parser ([#433](https://github.com/babel/babylon/pull/433)) (Alex Kuzmenko) + * Ensure non pattern shorthand props are checked for reserved words ([#479](https://github.com/babel/babylon/pull/479)) (Brian Ng) + * Remove jsx context when parsing arrow functions ([#475](https://github.com/babel/babylon/pull/475)) (Brian Ng) + * Allow super in class properties ([#499](https://github.com/babel/babylon/pull/499)) (Brian Ng) + * Allow flow class field to be named constructor ([#510](https://github.com/babel/babylon/pull/510)) (Brian Ng) + +## 6.17.0 (2017-04-20) + +### :bug: Bug Fix + * Cherry-pick #418 to 6.x ([#476](https://github.com/babel/babylon/pull/476)) (Sebastian McKenzie) + * Add support for invalid escapes in tagged templates ([#274](https://github.com/babel/babylon/pull/274)) (Kevin Gibbons) + * Throw error if new.target is used outside of a function ([#402](https://github.com/babel/babylon/pull/402)) (Brian Ng) + * Fix parsing of class properties ([#351](https://github.com/babel/babylon/pull/351)) (Kevin Gibbons) + * Fix parsing yield with dynamicImport ([#383](https://github.com/babel/babylon/pull/383)) (Brian Ng) + * Ensure consistent start args for parseParenItem ([#386](https://github.com/babel/babylon/pull/386)) (Brian Ng) + +## 7.0.0-beta.8 (2017-04-04) + +### New Feature +* Add support for flow type spread (#418) (Conrad Buck) +* Allow statics in flow interfaces (#427) (Brian Ng) + +### Bug Fix +* Fix predicate attachment to match flow parser (#428) (Brian Ng) +* Add extra.raw back to JSXText and JSXAttribute (#344) (Alex Rattray) +* Fix rest parameters with array and objects (#424) (Brian Ng) +* Fix number parser (#433) (Alex Kuzmenko) + +### Docs +* Fix CONTRIBUTING.md [skip ci] (#432) (Alex Kuzmenko) + +### Internal +* Use babel-register script when running babel smoke tests (#442) (Brian Ng) + +## 7.0.0-beta.7 (2017-03-22) + +### Spec Compliance +* Remove babylon plugin for template revision since it's stage-4 (#426) (Henry Zhu) + +### Bug Fix + +* Fix push-pop logic in flow (#405) (Daniel Tschinder) + +## 7.0.0-beta.6 (2017-03-21) + +### New Feature +* Add support for invalid escapes in tagged templates (#274) (Kevin Gibbons) + +### Polish +* Improves error message when super is called outside of constructor (#408) (Arshabh Kumar Agarwal) + +### Docs + +* [7.0] Moved value field in spec from ObjectMember to ObjectProperty as ObjectMethod's don't have it (#415) [skip ci] (James Browning) + +## 7.0.0-beta.5 (2017-03-21) + +### Bug Fix +* Throw error if new.target is used outside of a function (#402) (Brian Ng) +* Fix parsing of class properties (#351) (Kevin Gibbons) + +### Other + * Test runner: Detect extra property in 'actual' but not in 'expected'. (#407) (Andy) + * Optimize travis builds (#419) (Daniel Tschinder) + * Update codecov to 2.0 (#412) (Daniel Tschinder) + * Fix spec for ClassMethod: It doesn't have a function, it *is* a function. (#406) [skip ci] (Andy) + * Changed Non-existent RestPattern to RestElement which is what is actually parsed (#409) [skip ci] (James Browning) + * Upgrade flow to 0.41 (Daniel Tschinder) + * Fix watch command (#403) (Brian Ng) + * Update yarn lock (Daniel Tschinder) + * Fix watch command (#403) (Brian Ng) + * chore(package): update flow-bin to version 0.41.0 (#395) (greenkeeper[bot]) + * Add estree test for correct order of directives (Daniel Tschinder) + * Add DoExpression to spec (#364) (Alex Kuzmenko) + * Mention cloning of repository in CONTRIBUTING.md (#391) [skip ci] (Sumedh Nimkarde) + * Explain how to run only one test (#389) [skip ci] (Aaron Ang) + + ## 7.0.0-beta.4 (2017-03-01) + +* Don't consume async when checking for async func decl (#377) (Brian Ng) +* add `ranges` option [skip ci] (Henry Zhu) +* Don't parse class properties without initializers when classProperties is disabled and Flow is enabled (#300) (Andrew Levine) + +## 7.0.0-beta.3 (2017-02-28) + +- [7.0] Change RestProperty/SpreadProperty to RestElement/SpreadElement (#384) +- Merge changes from 6.x + +## 7.0.0-beta.2 (2017-02-20) + +- estree: correctly change literals in all cases (#368) (Daniel Tschinder) + +## 7.0.0-beta.1 (2017-02-20) + +- Fix negative number literal typeannotations (#366) (Daniel Tschinder) +- Update contributing with more test info [skip ci] (#355) (Brian Ng) + +## 7.0.0-beta.0 (2017-02-15) + +- Reintroduce Variance node (#333) (Daniel Tschinder) +- Rename NumericLiteralTypeAnnotation to NumberLiteralTypeAnnotation (#332) (Charles Pick) +- [7.0] Remove ForAwaitStatement, add await flag to ForOfStatement (#349) (Brandon Dail) +- chore(package): update ava to version 0.18.0 (#345) (greenkeeper[bot]) +- chore(package): update babel-plugin-istanbul to version 4.0.0 (#350) (greenkeeper[bot]) +- Change location of ObjectTypeIndexer to match flow (#228) (Daniel Tschinder) +- Rename flow AST Type ExistentialTypeParam to ExistsTypeAnnotation (#322) (Toru Kobayashi) +- Revert "Temporary rollback for erroring on trailing comma with spread (#154)" (#290) (Daniel Tschinder) +- Remove classConstructorCall plugin (#291) (Brian Ng) +- Update yarn.lock (Daniel Tschinder) +- Update cross-env to 3.x (Daniel Tschinder) +- [7.0] Remove node 0.10, 0.12 and 5 from Travis (#284) (Sergey Rubanov) +- Remove `String.fromCodePoint` shim (#279) (Mathias Bynens) + +## 6.16.1 (2017-02-23) + +### :bug: Regression + +- Revert "Fix export default async function to be FunctionDeclaration" ([#375](https://github.com/babel/babylon/pull/375)) + +Need to modify Babel for this AST node change, so moving to 7.0. + +- Revert "Don't parse class properties without initializers when classProperties plugin is disabled, and Flow is enabled" ([#376](https://github.com/babel/babylon/pull/376)) + +[react-native](https://github.com/facebook/react-native/issues/12542) broke with this so we reverted. + +## 6.16.0 (2017-02-23) + +### :rocket: New Feature + +***ESTree*** compatibility as plugin ([#277](https://github.com/babel/babylon/pull/277)) (Daniel Tschinder) + +We finally introduce a new compatibility layer for ESTree. To put babylon into ESTree-compatible mode the new plugin `estree` can be enabled. In this mode the parser will output an AST that is compliant to the specs of [ESTree](https://github.com/estree/estree/) + +We highly recommend everyone who uses babylon outside of babel to use this plugin. This will make it much easier for users to switch between different ESTree-compatible parsers. We so far tested several projects with different parsers and exchanged their parser to babylon and in nearly all cases it worked out of the box. Some other estree-compatible parsers include `acorn`, `esprima`, `espree`, `flow-parser`, etc. + +To enable `estree` mode simply add the plugin in the config: +```json +{ + "plugins": [ "estree" ] +} +``` + +If you want to migrate your project from non-ESTree mode to ESTree, have a look at our [Readme](https://github.com/babel/babylon/#output), where all deviations are mentioned. + +Add a parseExpression public method ([#213](https://github.com/babel/babylon/pull/213)) (jeromew) + +Babylon exports a new function to parse a single expression + +```js +import { parseExpression } from 'babylon'; + +const ast = parseExpression('x || y && z', options); +``` + +The returned AST will only consist of the expression. The options are the same as for `parse()` + +Add startLine option ([#346](https://github.com/babel/babylon/pull/346)) (Raphael Mu) + +A new option was added to babylon allowing to change the initial linenumber for the first line which is usually `1`. +Changing this for example to `100` will make line `1` of the input source to be marked as line `100`, line `2` as `101`, line `3` as `102`, ... + +Function predicate declaration ([#103](https://github.com/babel/babylon/pull/103)) (Panagiotis Vekris) + +Added support for function predicates which flow introduced in version 0.33.0 + +```js +declare function is_number(x: mixed): boolean %checks(typeof x === "number"); +``` + +Allow imports in declare module ([#315](https://github.com/babel/babylon/pull/315)) (Daniel Tschinder) + +Added support for imports within module declarations which flow introduced in version 0.37.0 + +```js +declare module "C" { + import type { DT } from "D"; + declare export type CT = { D: DT }; +} +``` + +### :eyeglasses: Spec Compliance + +Forbid semicolons after decorators in classes ([#352](https://github.com/babel/babylon/pull/352)) (Kevin Gibbons) + +This example now correctly throws an error when there is a semicolon after the decorator: + +```js +class A { +@a; +foo(){} +} +``` + +Keywords are not allowed as local specifier ([#307](https://github.com/babel/babylon/pull/307)) (Daniel Tschinder) + +Using keywords in imports is not allowed anymore: + +```js +import { default } from "foo"; +import { a as debugger } from "foo"; +``` + +Do not allow overwritting of primitive types ([#314](https://github.com/babel/babylon/pull/314)) (Daniel Tschinder) + +In flow it is now forbidden to overwrite the primitive types `"any"`, `"mixed"`, `"empty"`, `"bool"`, `"boolean"`, `"number"`, `"string"`, `"void"` and `"null"` with your own type declaration. + +Disallow import type { type a } from … ([#305](https://github.com/babel/babylon/pull/305)) (Daniel Tschinder) + +The following code now correctly throws an error + +```js +import type { type a } from "foo"; +``` + +Don't parse class properties without initializers when classProperties is disabled and Flow is enabled ([#300](https://github.com/babel/babylon/pull/300)) (Andrew Levine) + +Ensure that you enable the `classProperties` plugin in order to enable correct parsing of class properties. Prior to this version it was possible to parse them by enabling the `flow` plugin but this was not intended the behaviour. + +If you enable the flow plugin you can only define the type of the class properties, but not initialize them. + +Fix export default async function to be FunctionDeclaration ([#324](https://github.com/babel/babylon/pull/324)) (Daniel Tschinder) + +Parsing the following code now returns a `FunctionDeclaration` AST node instead of `FunctionExpression`. + +```js +export default async function bar() {}; +``` + +### :nail_care: Polish + +Improve error message on attempt to destructure named import ([#288](https://github.com/babel/babylon/pull/288)) (Brian Ng) + +### :bug: Bug Fix + +Fix negative number literal typeannotations ([#366](https://github.com/babel/babylon/pull/366)) (Daniel Tschinder) + +Ensure takeDecorators is called on exported class ([#358](https://github.com/babel/babylon/pull/358)) (Brian Ng) + +ESTree: correctly change literals in all cases ([#368](https://github.com/babel/babylon/pull/368)) (Daniel Tschinder) + +Correctly convert RestProperty to Assignable ([#339](https://github.com/babel/babylon/pull/339)) (Daniel Tschinder) + +Fix #321 by allowing question marks in type params ([#338](https://github.com/babel/babylon/pull/338)) (Daniel Tschinder) + +Fix #336 by correctly setting arrow-param ([#337](https://github.com/babel/babylon/pull/337)) (Daniel Tschinder) + +Fix parse error when destructuring `set` with default value ([#317](https://github.com/babel/babylon/pull/317)) (Brian Ng) + +Fix ObjectTypeCallProperty static ([#298](https://github.com/babel/babylon/pull/298)) (Dan Harper) + + +### :house: Internal + +Fix generator-method-with-computed-name spec ([#360](https://github.com/babel/babylon/pull/360)) (Alex Rattray) + +Fix flow type-parameter-declaration test with unintended semantic ([#361](https://github.com/babel/babylon/pull/361)) (Alex Rattray) + +Cleanup and splitup parser functions ([#295](https://github.com/babel/babylon/pull/295)) (Daniel Tschinder) + +chore(package): update flow-bin to version 0.38.0 ([#313](https://github.com/babel/babylon/pull/313)) (greenkeeper[bot]) + +Call inner function instead of 1:1 copy to plugin ([#294](https://github.com/babel/babylon/pull/294)) (Daniel Tschinder) + +Update eslint-config-babel to the latest version 🚀 ([#299](https://github.com/babel/babylon/pull/299)) (greenkeeper[bot]) + +Update eslint-config-babel to the latest version 🚀 ([#293](https://github.com/babel/babylon/pull/293)) (greenkeeper[bot]) + +devDeps: remove eslint-plugin-babel ([#292](https://github.com/babel/babylon/pull/292)) (Kai Cataldo) + +Correct indent eslint rule config ([#276](https://github.com/babel/babylon/pull/276)) (Daniel Tschinder) + +Fail tests that have expected.json and throws-option ([#285](https://github.com/babel/babylon/pull/285)) (Daniel Tschinder) + +### :memo: Documentation + +Update contributing with more test info [skip ci] ([#355](https://github.com/babel/babylon/pull/355)) (Brian Ng) + +Update API documentation ([#330](https://github.com/babel/babylon/pull/330)) (Timothy Gu) + +Added keywords to package.json ([#323](https://github.com/babel/babylon/pull/323)) (Dmytro) + +AST spec: fix casing of `RegExpLiteral` ([#318](https://github.com/babel/babylon/pull/318)) (Mathias Bynens) + +## 6.15.0 (2017-01-10) + +### :eyeglasses: Spec Compliance + +Add support for Flow shorthand import type ([#267](https://github.com/babel/babylon/pull/267)) (Jeff Morrison) + +This change implements flows new shorthand import syntax +and where previously you had to write this code: + +```js +import {someValue} from "blah"; +import type {someType} from "blah"; +import typeof {someOtherValue} from "blah"; +``` + +you can now write it like this: + +```js +import { + someValue, + type someType, + typeof someOtherValue, +} from "blah"; +``` + +For more information look at [this](https://github.com/facebook/flow/pull/2890) pull request. + +flow: allow leading pipes in all positions ([#256](https://github.com/babel/babylon/pull/256)) (Vladimir Kurchatkin) + +This change now allows a leading pipe everywhere types can be used: +```js +var f = (x): | 1 | 2 => 1; +``` + +Throw error when exporting non-declaration ([#241](https://github.com/babel/babylon/pull/241)) (Kai Cataldo) + +Previously babylon parsed the following exports, although they are not valid: +```js +export typeof foo; +export new Foo(); +export function() {}; +export for (;;); +export while(foo); +``` + +### :bug: Bug Fix + +Don't set inType flag when parsing property names ([#266](https://github.com/babel/babylon/pull/266)) (Vladimir Kurchatkin) + +This fixes parsing of this case: + +```js +const map = { + [age <= 17] : 'Too young' +}; +``` + +Fix source location for JSXEmptyExpression nodes (fixes #248) ([#249](https://github.com/babel/babylon/pull/249)) (James Long) + +The following case produced an invalid AST +```js +
{/* foo */}
+``` + +Use fromCodePoint to convert high value unicode entities ([#243](https://github.com/babel/babylon/pull/243)) (Ryan Duffy) + +When high value unicode entities (e.g. 💩) were used in the input source code they are now correctly encoded in the resulting AST. + +Rename folder to avoid Windows-illegal characters ([#281](https://github.com/babel/babylon/pull/281)) (Ryan Plant) + +Allow this.state.clone() when parsing decorators ([#262](https://github.com/babel/babylon/pull/262)) (Alex Rattray) + +### :house: Internal + +User external-helpers ([#254](https://github.com/babel/babylon/pull/254)) (Daniel Tschinder) + +Add watch script for dev ([#234](https://github.com/babel/babylon/pull/234)) (Kai Cataldo) + +Freeze current plugins list for "*" option, and remove from README.md ([#245](https://github.com/babel/babylon/pull/245)) (Andrew Levine) + +Prepare tests for multiple fixture runners. ([#240](https://github.com/babel/babylon/pull/240)) (Daniel Tschinder) + +Add some test coverage for decorators stage-0 plugin ([#250](https://github.com/babel/babylon/pull/250)) (Andrew Levine) + +Refactor tokenizer types file ([#263](https://github.com/babel/babylon/pull/263)) (Sven SAULEAU) + +Update eslint-config-babel to the latest version 🚀 ([#273](https://github.com/babel/babylon/pull/273)) (greenkeeper[bot]) + +chore(package): update rollup to version 0.41.0 ([#272](https://github.com/babel/babylon/pull/272)) (greenkeeper[bot]) + +chore(package): update flow-bin to version 0.37.0 ([#255](https://github.com/babel/babylon/pull/255)) (greenkeeper[bot]) + +## 6.14.1 (2016-11-17) + +### :bug: Bug Fix + +Allow `"plugins": ["*"]` ([#229](https://github.com/babel/babylon/pull/229)) (Daniel Tschinder) + +```js +{ + "plugins": ["*"] +} +``` + +Will include all parser plugins instead of specifying each one individually. Useful for tools like babel-eslint, jscodeshift, and ast-explorer. + +## 6.14.0 (2016-11-16) + +### :eyeglasses: Spec Compliance + +Throw error for reserved words `enum` and `await` ([#195](https://github.com/babel/babylon/pull/195)) (Kai Cataldo) + +[11.6.2.2 Future Reserved Words](http://www.ecma-international.org/ecma-262/6.0/#sec-future-reserved-words) + +Babylon will throw for more reserved words such as `enum` or `await` (in strict mode). + +``` +class enum {} // throws +class await {} // throws in strict mode (module) +``` + +Optional names for function types and object type indexers ([#197](https://github.com/babel/babylon/pull/197)) (Gabe Levi) + +So where you used to have to write + +```js +type A = (x: string, y: boolean) => number; +type B = (z: string) => number; +type C = { [key: string]: number }; +``` + +you can now write (with flow 0.34.0) + +```js +type A = (string, boolean) => number; +type B = string => number; +type C = { [string]: number }; +``` + +Parse flow nested array type annotations like `number[][]` ([#219](https://github.com/babel/babylon/pull/219)) (Bernhard Häussner) + +Supports these form now of specifying array types: + +```js +var a: number[][][][]; +var b: string[][]; +``` + +### :bug: Bug Fix + +Correctly eat semicolon at the end of `DelcareModuleExports` ([#223](https://github.com/babel/babylon/pull/223)) (Daniel Tschinder) + +``` +declare module "foo" { declare module.exports: number } +declare module "foo" { declare module.exports: number; } // also allowed now +``` + +### :house: Internal + + * Count Babel tests towards Babylon code coverage ([#182](https://github.com/babel/babylon/pull/182)) (Moti Zilberman) + * Fix strange line endings ([#214](https://github.com/babel/babylon/pull/214)) (Thomas Grainger) + * Add node 7 (Daniel Tschinder) + * chore(package): update flow-bin to version 0.34.0 ([#204](https://github.com/babel/babylon/pull/204)) (Greenkeeper) + +## v6.13.1 (2016-10-26) + +### :nail_care: Polish + +- Use rollup for bundling to speed up startup time ([#190](https://github.com/babel/babylon/pull/190)) ([@drewml](https://github.com/DrewML)) + +```js +const babylon = require('babylon'); +const ast = babylon.parse('var foo = "lol";'); +``` + +With that test case, there was a ~95ms savings by removing the need for node to build/traverse the dependency graph. + +**Without bundling** +![image](https://cloud.githubusercontent.com/assets/5233399/19420264/3133497e-93ad-11e6-9a6a-2da59c4f5c13.png) + +**With bundling** +![image](https://cloud.githubusercontent.com/assets/5233399/19420267/388f556e-93ad-11e6-813e-7c5c396be322.png) + +- add clean command [skip ci] ([#201](https://github.com/babel/babylon/pull/201)) (Henry Zhu) +- add ForAwaitStatement (async generator already added) [skip ci] ([#196](https://github.com/babel/babylon/pull/196)) (Henry Zhu) + +## v6.13.0 (2016-10-21) + +### :eyeglasses: Spec Compliance + +Property variance type annotations for Flow plugin ([#161](https://github.com/babel/babylon/pull/161)) (Sam Goldman) + +> See https://flowtype.org/docs/variance.html for more information + +```js +type T = { +p: T }; +interface T { -p: T }; +declare class T { +[k:K]: V }; +class T { -[k:K]: V }; +class C2 { +p: T = e }; +``` + +Raise error on duplicate definition of __proto__ ([#183](https://github.com/babel/babylon/pull/183)) (Moti Zilberman) + +```js +({ __proto__: 1, __proto__: 2 }) // Throws an error now +``` + +### :bug: Bug Fix + +Flow: Allow class properties to be named `static` ([#184](https://github.com/babel/babylon/pull/184)) (Moti Zilberman) + +```js +declare class A { + static: T; +} +``` + +Allow "async" as identifier for object literal property shorthand ([#187](https://github.com/babel/babylon/pull/187)) (Andrew Levine) + +```js +var foo = { async, bar }; +``` + +### :nail_care: Polish + +Fix flowtype and add inType to state ([#189](https://github.com/babel/babylon/pull/189)) (Daniel Tschinder) + +> This improves the performance slightly (because of hidden classes) + +### :house: Internal + +Fix .gitattributes line ending setting ([#191](https://github.com/babel/babylon/pull/191)) (Moti Zilberman) + +Increase test coverage ([#175](https://github.com/babel/babylon/pull/175) (Moti Zilberman) + +Readd missin .eslinignore for IDEs (Daniel Tschinder) + +Error on missing expected.json fixture in CI ([#188](https://github.com/babel/babylon/pull/188)) (Moti Zilberman) + +Add .gitattributes and .editorconfig for LF line endings ([#179](https://github.com/babel/babylon/pull/179)) (Moti Zilberman) + +Fixes two tests that are failing after the merge of #172 ([#177](https://github.com/babel/babylon/pull/177)) (Moti Zilberman) + +## v6.12.0 (2016-10-14) + +### :eyeglasses: Spec Compliance + +Implement import() syntax ([#163](https://github.com/babel/babylon/pull/163)) (Jordan Gensler) + +#### Dynamic Import + +- Proposal Repo: https://github.com/domenic/proposal-dynamic-import +- Championed by [@domenic](https://github.com/domenic) +- stage-2 +- [sept-28 tc39 notes](https://github.com/rwaldron/tc39-notes/blob/master/es7/2016-09/sept-28.md#113a-import) + +> This repository contains a proposal for adding a "function-like" import() module loading syntactic form to JavaScript + +```js +import(`./section-modules/${link.dataset.entryModule}.js`) +.then(module => { + module.loadPageInto(main); +}) +``` + +Add EmptyTypeAnnotation ([#171](https://github.com/babel/babylon/pull/171)) (Sam Goldman) + +#### EmptyTypeAnnotation + +Just wasn't covered before. + +```js +type T = empty; +``` + +### :bug: Bug Fix + +Fix crash when exporting with destructuring and sparse array ([#170](https://github.com/babel/babylon/pull/170)) (Jeroen Engels) + +```js +// was failing due to sparse array +export const { foo: [ ,, qux7 ] } = bar; +``` + +Allow keyword in Flow object declaration property names with type parameters ([#146](https://github.com/babel/babylon/pull/146)) (Dan Harper) + +```js +declare class X { + foobar(): void; + static foobar(): void; +} +``` + +Allow keyword in object/class property names with Flow type parameters ([#145](https://github.com/babel/babylon/pull/145)) (Dan Harper) + +```js +class Foo { + delete(item: T): T { + return item; + } +} +``` + +Allow typeAnnotations for yield expressions ([#174](https://github.com/babel/babylon/pull/174))) (Daniel Tschinder) + +```js +function *foo() { + const x = (yield 5: any); +} +``` + +### :nail_care: Polish + +Annotate more errors with expected token ([#172](https://github.com/babel/babylon/pull/172))) (Moti Zilberman) + +```js +// Unexpected token, expected ; (1:6) +{ set 1 } +``` + +### :house: Internal + +Remove kcheck ([#173](https://github.com/babel/babylon/pull/173))) (Daniel Tschinder) + +Also run flow, linting, babel tests on separate instances (add back node 0.10) + +## v6.11.6 (2016-10-12) + +### :bug: Bug Fix/Regression + +Fix crash when exporting with destructuring and sparse array ([#170](https://github.com/babel/babylon/pull/170)) (Jeroen Engels) + +```js +// was failing with `Cannot read property 'type' of null` because of null identifiers +export const { foo: [ ,, qux7 ] } = bar; +``` + +## v6.11.5 (2016-10-12) + +### :eyeglasses: Spec Compliance + +Fix: Check for duplicate named exports in exported destructuring assignments ([#144](https://github.com/babel/babylon/pull/144)) (Kai Cataldo) + +```js +// `foo` has already been exported. Exported identifiers must be unique. (2:20) +export function foo() {}; +export const { a: [{foo}] } = bar; +``` + +Fix: Check for duplicate named exports in exported rest elements/properties ([#164](https://github.com/babel/babylon/pull/164)) (Kai Cataldo) + +```js +// `foo` has already been exported. Exported identifiers must be unique. (2:22) +export const foo = 1; +export const [bar, ...foo] = baz; +``` + +### :bug: Bug Fix + +Fix: Allow identifier `async` for default param in arrow expression ([#165](https://github.com/babel/babylon/pull/165)) (Kai Cataldo) + +```js +// this is ok now +const test = ({async = true}) => {}; +``` + +### :nail_care: Polish + +Babylon will now print out the token it's expecting if there's a `SyntaxError` ([#150](https://github.com/babel/babylon/pull/150)) (Daniel Tschinder) + +```bash +# So in the case of a missing ending curly (`}`) +Module build failed: SyntaxError: Unexpected token, expected } (30:0) + 28 | } + 29 | +> 30 | + | ^ +``` + +## v6.11.4 (2016-10-03) + +Temporary rollback for erroring on trailing comma with spread (#154) (Henry Zhu) + +## v6.11.3 (2016-10-01) + +### :eyeglasses: Spec Compliance + +Add static errors for object rest (#149) ([@danez](https://github.com/danez)) + +> https://github.com/sebmarkbage/ecmascript-rest-spread + +Object rest copies the *rest* of properties from the right hand side `obj` starting from the left to right. + +```js +let { x, y, ...z } = { x: 1, y: 2, z: 3 }; +// x = 1 +// y = 2 +// z = { z: 3 } +``` + +#### New Syntax Errors: + +**SyntaxError**: The rest element has to be the last element when destructuring (1:10) +```bash +> 1 | let { ...x, y, z } = { x: 1, y: 2, z: 3}; + | ^ +# Previous behavior: +# x = { x: 1, y: 2, z: 3 } +# y = 2 +# z = 3 +``` + +Before, this was just a more verbose way of shallow copying `obj` since it doesn't actually do what you think. + +**SyntaxError**: Cannot have multiple rest elements when destructuring (1:13) + +```bash +> 1 | let { x, ...y, ...z } = { x: 1, y: 2, z: 3}; + | ^ +# Previous behavior: +# x = 1 +# y = { y: 2, z: 3 } +# z = { y: 2, z: 3 } +``` + +Before y and z would just be the same value anyway so there is no reason to need to have both. + +**SyntaxError**: A trailing comma is not permitted after the rest element (1:16) + +```js +let { x, y, ...z, } = obj; +``` + +The rationale for this is that the use case for trailing comma is that you can add something at the end without affecting the line above. Since a RestProperty always has to be the last property it doesn't make sense. + +--- + +get / set are valid property names in default assignment (#142) ([@jezell](https://github.com/jezell)) + +```js +// valid +function something({ set = null, get = null }) {} +``` + +## v6.11.2 (2016-09-23) + +### Bug Fix + +- [#139](https://github.com/babel/babylon/issues/139) Don't do the duplicate check if not an identifier (#140) @hzoo + +```js +// regression with duplicate export check +SyntaxError: ./typography.js: `undefined` has already been exported. Exported identifiers must be unique. (22:13) + 20 | + 21 | export const { rhythm } = typography; +> 22 | export const { TypographyStyle } = typography +``` + +Bail out for now, and make a change to account for destructuring in the next release. + +## 6.11.1 (2016-09-22) + +### Bug Fix +- [#137](https://github.com/babel/babylon/pull/137) - Fix a regression with duplicate exports - it was erroring on all keys in `Object.prototype`. @danez + +```javascript +export toString from './toString'; +``` + +```bash +`toString` has already been exported. Exported identifiers must be unique. (1:7) +> 1 | export toString from './toString'; + | ^ + 2 | +``` + +## 6.11.0 (2016-09-22) + +### Spec Compliance (will break CI) + +- Disallow duplicate named exports ([#107](https://github.com/babel/babylon/pull/107)) @kaicataldo + +```js +// Only one default export allowed per module. (2:9) +export default function() {}; +export { foo as default }; + +// Only one default export allowed per module. (2:0) +export default {}; +export default function() {}; + +// `Foo` has already been exported. Exported identifiers must be unique. (2:0) +export { Foo }; +export class Foo {}; +``` + +### New Feature (Syntax) + +- Add support for computed class property names ([#121](https://github.com/babel/babylon/pull/121)) @motiz88 + +```js +// AST +interface ClassProperty <: Node { + type: "ClassProperty"; + key: Identifier; + value: Expression; + computed: boolean; // added +} +``` + +```js +// with "plugins": ["classProperties"] +class Foo { + [x] + ['y'] +} + +class Bar { + [p] + [m] () {} +} + ``` + +### Bug Fix + +- Fix `static` property falling through in the declare class Flow AST ([#135](https://github.com/babel/babylon/pull/135)) @danharper + +```js +declare class X { + a: number; + static b: number; // static + c: number; // this was being marked as static in the AST as well +} +``` + +### Polish + +- Rephrase "assigning/binding to rvalue" errors to include context ([#119](https://github.com/babel/babylon/pull/119)) @motiz88 + +```js +// Used to error with: +// SyntaxError: Assigning to rvalue (1:0) + +// Now: +// Invalid left-hand side in assignment expression (1:0) +3 = 4 + +// Invalid left-hand side in for-in statement (1:5) +for (+i in {}); +``` + +### Internal + +- Fix call to `this.parseMaybeAssign` with correct arguments ([#133](https://github.com/babel/babylon/pull/133)) @danez +- Add semver note to changelog ([#131](https://github.com/babel/babylon/pull/131)) @hzoo + +## 6.10.0 (2016-09-19) + +> We plan to include some spec compliance bugs in patch versions. An example was the multiple default exports issue. + +### Spec Compliance + +* Implement ES2016 check for simple parameter list in strict mode ([#106](https://github.com/babel/babylon/pull/106)) (Timothy Gu) + +> It is a Syntax Error if ContainsUseStrict of FunctionBody is true and IsSimpleParameterList of FormalParameters is false. https://tc39.github.io/ecma262/2016/#sec-function-definitions-static-semantics-early-errors + +More Context: [tc39-notes](https://github.com/rwaldron/tc39-notes/blob/master/es7/2015-07/july-29.md#611-the-scope-of-use-strict-with-respect-to-destructuring-in-parameter-lists) + +For example: + +```js +// this errors because it uses destructuring and default parameters +// in a function with a "use strict" directive +function a([ option1, option2 ] = []) { + "use strict"; +} + ``` + +The solution would be to use a top level "use strict" or to remove the destructuring or default parameters when using a function + "use strict" or to. + +### New Feature + +* Exact object type annotations for Flow plugin ([#104](https://github.com/babel/babylon/pull/104)) (Basil Hosmer) + +Added to flow in https://github.com/facebook/flow/commit/c710c40aa2a115435098d6c0dfeaadb023cd39b8 + +Looks like: + +```js +var a : {| x: number, y: string |} = { x: 0, y: 'foo' }; +``` + +### Bug Fixes + +* Include `typeParameter` location in `ArrowFunctionExpression` ([#126](https://github.com/babel/babylon/pull/126)) (Daniel Tschinder) +* Error on invalid flow type annotation with default assignment ([#122](https://github.com/babel/babylon/pull/122)) (Dan Harper) +* Fix Flow return types on arrow functions ([#124](https://github.com/babel/babylon/pull/124)) (Dan Harper) + +### Misc + +* Add tests for export extensions ([#127](https://github.com/babel/babylon/pull/127)) (Daniel Tschinder) +* Fix Contributing guidelines [skip ci] (Daniel Tschinder) + +## 6.9.2 (2016-09-09) + +The only change is to remove the `babel-runtime` dependency by compiling with Babel's ES2015 loose mode. So using babylon standalone should be smaller. + +## 6.9.1 (2016-08-23) + +This release contains mainly small bugfixes but also updates babylons default mode to es2017. The features for `exponentiationOperator`, `asyncFunctions` and `trailingFunctionCommas` which previously needed to be activated via plugin are now enabled by default and the plugins are now no-ops. + +### Bug Fixes + +- Fix issues with default object params in async functions ([#96](https://github.com/babel/babylon/pull/96)) @danez +- Fix issues with flow-types and async function ([#95](https://github.com/babel/babylon/pull/95)) @danez +- Fix arrow functions with destructuring, types & default value ([#94](https://github.com/babel/babylon/pull/94)) @danharper +- Fix declare class with qualified type identifier ([#97](https://github.com/babel/babylon/pull/97)) @danez +- Remove exponentiationOperator, asyncFunctions, trailingFunctionCommas plugins and enable them by default ([#98](https://github.com/babel/babylon/pull/98)) @danez + +## 6.9.0 (2016-08-16) + +### New syntax support + +- Add JSX spread children ([#42](https://github.com/babel/babylon/pull/42)) @calebmer + +(Be aware that React is not going to support this syntax) + +```js +
+ {...todos.map(todo => )} +
+``` + +- Add support for declare module.exports ([#72](https://github.com/babel/babylon/pull/72)) @danez + +```js +declare module "foo" { + declare module.exports: {} +} +``` + +### New Features + +- If supplied, attach filename property to comment node loc. ([#80](https://github.com/babel/babylon/pull/80)) @divmain +- Add identifier name to node loc field ([#90](https://github.com/babel/babylon/pull/90)) @kittens + +### Bug Fixes + +- Fix exponential operator to behave according to spec ([#75](https://github.com/babel/babylon/pull/75)) @danez +- Fix lookahead to not add comments to arrays which are not cloned ([#76](https://github.com/babel/babylon/pull/76)) @danez +- Fix accidental fall-through in Flow type parsing. ([#82](https://github.com/babel/babylon/pull/82)) @xiemaisi +- Only allow declares inside declare module ([#73](https://github.com/babel/babylon/pull/73)) @danez +- Small fix for parsing type parameter declarations ([#83](https://github.com/babel/babylon/pull/83)) @gabelevi +- Fix arrow param locations with flow types ([#57](https://github.com/babel/babylon/pull/57)) @danez +- Fixes SyntaxError position with flow optional type ([#65](https://github.com/babel/babylon/pull/65)) @danez + +### Internal + +- Add codecoverage to tests @danez +- Fix tests to not save expected output if we expect the test to fail @danez +- Make a shallow clone of babel for testing @danez +- chore(package): update cross-env to version 2.0.0 ([#77](https://github.com/babel/babylon/pull/77)) @greenkeeperio-bot +- chore(package): update ava to version 0.16.0 ([#86](https://github.com/babel/babylon/pull/86)) @greenkeeperio-bot +- chore(package): update babel-plugin-istanbul to version 2.0.0 ([#89](https://github.com/babel/babylon/pull/89)) @greenkeeperio-bot +- chore(package): update nyc to version 8.0.0 ([#88](https://github.com/babel/babylon/pull/88)) @greenkeeperio-bot + +## 6.8.4 (2016-07-06) + +### Bug Fixes + +- Fix the location of params, when flow and default value used ([#68](https://github.com/babel/babylon/pull/68)) @danez + +## 6.8.3 (2016-07-02) + +### Bug Fixes + +- Fix performance regression introduced in 6.8.2 with conditionals ([#63](https://github.com/babel/babylon/pull/63)) @danez + +## 6.8.2 (2016-06-24) + +### Bug Fixes + +- Fix parse error with yielding jsx elements in generators `function* it() { yield ; }` ([#31](https://github.com/babel/babylon/pull/31)) @eldereal +- When cloning nodes do not clone its comments ([#24](https://github.com/babel/babylon/pull/24)) @danez +- Fix parse errors when using arrow functions with an spread element and return type `(...props): void => {}` ([#10](https://github.com/babel/babylon/pull/10)) @danez +- Fix leading comments added from previous node ([#23](https://github.com/babel/babylon/pull/23)) @danez +- Fix parse errors with flow's optional arguments `(arg?) => {}` ([#19](https://github.com/babel/babylon/pull/19)) @danez +- Support negative numeric type literals @kittens +- Remove line terminator restriction after await keyword @kittens +- Remove grouped type arrow restriction as it seems flow no longer has it @kittens +- Fix parse error with generic methods that have the name `get` or `set` `class foo { get() {} }` ([#55](https://github.com/babel/babylon/pull/55)) @vkurchatkin +- Fix parse error with arrow functions that have flow type parameter declarations `(x: T): T => x;` ([#54](https://github.com/babel/babylon/pull/54)) @gabelevi + +### Documentation + +- Document AST differences from ESTree ([#41](https://github.com/babel/babylon/pull/41)) @nene +- Move ast spec from babel/babel ([#46](https://github.com/babel/babylon/pull/46)) @hzoo + +### Internal + +- Enable skipped tests ([#16](https://github.com/babel/babylon/pull/16)) @danez +- Add script to test latest version of babylon with babel ([#21](https://github.com/babel/babylon/pull/21)) @danez +- Upgrade test runner ava @kittens +- Add missing generate-identifier-regex script @kittens +- Rename parser context types @kittens +- Add node v6 to travis testing @hzoo +- Update to Unicode v9 ([#45](https://github.com/babel/babylon/pull/45)) @mathiasbynens + +## 6.8.1 (2016-06-06) + +### New Feature + +- Parse type parameter declarations with defaults like `type Foo = T` + +### Bug Fixes +- Type parameter declarations need 1 or more type parameters. +- The existential type `*` is not a valid type parameter. +- The existential type `*` is a primary type + +### Spec Compliance +- The param list for type parameter declarations now consists of `TypeParameter` nodes +- New `TypeParameter` AST Node (replaces using the `Identifier` node before) + +``` +interface TypeParameter <: Node { + bound: TypeAnnotation; + default: TypeAnnotation; + name: string; + variance: "plus" | "minus"; +} +``` + +## 6.8.0 (2016-05-02) + +#### New Feature + +##### Parse Method Parameter Decorators ([#12](https://github.com/babel/babylon/pull/12)) + +> [Method Parameter Decorators](https://goo.gl/8MmCMG) is now a TC39 [stage 0 proposal](https://github.com/tc39/ecma262/blob/master/stage0.md). + +Examples: + +```js +class Foo { + constructor(@foo() x, @bar({ a: 123 }) @baz() y) {} +} + +export default function func(@foo() x, @bar({ a: 123 }) @baz() y) {} + +var obj = { + method(@foo() x, @bar({ a: 123 }) @baz() y) {} +}; +``` + +##### Parse for-await statements (w/ `asyncGenerators` plugin) ([#17](https://github.com/babel/babylon/pull/17)) + +There is also a new node type, `ForAwaitStatement`. + +> [Async generators and for-await](https://github.com/tc39/proposal-async-iteration) are now a [stage 2 proposal](https://github.com/tc39/ecma262#current-proposals). + +Example: + +```js +async function f() { + for await (let x of y); +} +``` diff --git a/backend/node_modules/@babel/parser/LICENSE b/backend/node_modules/@babel/parser/LICENSE new file mode 100644 index 00000000..d4c7fc58 --- /dev/null +++ b/backend/node_modules/@babel/parser/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012-2014 by various contributors (see AUTHORS) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/node_modules/@babel/parser/README.md b/backend/node_modules/@babel/parser/README.md new file mode 100644 index 00000000..a9463e81 --- /dev/null +++ b/backend/node_modules/@babel/parser/README.md @@ -0,0 +1,19 @@ +# @babel/parser + +> A JavaScript parser + +See our website [@babel/parser](https://babeljs.io/docs/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%22+is%3Aopen) associated with this package. + +## Install + +Using npm: + +```sh +npm install --save-dev @babel/parser +``` + +or using yarn: + +```sh +yarn add @babel/parser --dev +``` diff --git a/backend/node_modules/@babel/parser/bin/babel-parser.js b/backend/node_modules/@babel/parser/bin/babel-parser.js new file mode 100755 index 00000000..4808c5ee --- /dev/null +++ b/backend/node_modules/@babel/parser/bin/babel-parser.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node +/* eslint-disable no-var, unicorn/prefer-node-protocol */ + +var parser = require(".."); +var fs = require("fs"); + +var filename = process.argv[2]; +if (!filename) { + console.error("no filename specified"); +} else { + var file = fs.readFileSync(filename, "utf8"); + var ast = parser.parse(file); + + console.log(JSON.stringify(ast, null, " ")); +} diff --git a/backend/node_modules/@babel/parser/package.json b/backend/node_modules/@babel/parser/package.json new file mode 100644 index 00000000..7cfbe459 --- /dev/null +++ b/backend/node_modules/@babel/parser/package.json @@ -0,0 +1,50 @@ +{ + "name": "@babel/parser", + "version": "7.29.8", + "description": "A JavaScript parser", + "author": "The Babel Team (https://babel.dev/team)", + "homepage": "https://babel.dev/docs/en/next/babel-parser", + "bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "keywords": [ + "babel", + "javascript", + "parser", + "tc39", + "ecmascript", + "@babel/parser" + ], + "repository": { + "type": "git", + "url": "https://github.com/babel/babel.git", + "directory": "packages/babel-parser" + }, + "main": "./lib/index.js", + "types": "./typings/babel-parser.d.ts", + "files": [ + "bin", + "lib", + "typings/babel-parser.d.ts", + "index.cjs" + ], + "engines": { + "node": ">=6.0.0" + }, + "# dependencies": "This package doesn't actually have runtime dependencies. @babel/types is only needed for type definitions.", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "devDependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/helper-check-duplicate-nodes": "^7.29.7", + "@babel/helper-fixtures": "^7.29.7", + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "charcodes": "^0.2.0" + }, + "bin": "./bin/babel-parser.js", + "type": "commonjs" +} \ No newline at end of file diff --git a/backend/node_modules/@babel/parser/typings/babel-parser.d.ts b/backend/node_modules/@babel/parser/typings/babel-parser.d.ts new file mode 100644 index 00000000..8c6c4afb --- /dev/null +++ b/backend/node_modules/@babel/parser/typings/babel-parser.d.ts @@ -0,0 +1,262 @@ +// This file is auto-generated! Do not modify it directly. +// Run `yarn gulp bundle-dts` to re-generate it. +/* eslint-disable @typescript-eslint/consistent-type-imports, @typescript-eslint/no-redundant-type-constituents */ +import { File, Expression } from '@babel/types'; + +declare class Position { + line: number; + column: number; + index: number; + constructor(line: number, col: number, index: number); +} + +type SyntaxPlugin = "flow" | "typescript" | "jsx" | "functionBind" | "pipelineOperator" | "placeholders"; +type ParseErrorCode = "BABEL_PARSER_SYNTAX_ERROR" | "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED"; +interface ParseErrorSpecification { + code: ParseErrorCode; + reasonCode: string; + syntaxPlugin?: SyntaxPlugin; + missingPlugin?: string | string[]; + loc: Position; + details: ErrorDetails; + pos: number; +} +type ParseError$1 = SyntaxError & ParseErrorSpecification; + +type BABEL_8_BREAKING = false; +type IF_BABEL_7 = false extends BABEL_8_BREAKING ? V : never; + +type Plugin$1 = + | "asyncDoExpressions" + | IF_BABEL_7<"asyncGenerators"> + | IF_BABEL_7<"bigInt"> + | IF_BABEL_7<"classPrivateMethods"> + | IF_BABEL_7<"classPrivateProperties"> + | IF_BABEL_7<"classProperties"> + | IF_BABEL_7<"classStaticBlock"> + | IF_BABEL_7<"decimal"> + | "decorators-legacy" + | "deferredImportEvaluation" + | "decoratorAutoAccessors" + | "destructuringPrivate" + | IF_BABEL_7<"deprecatedImportAssert"> + | "doExpressions" + | IF_BABEL_7<"dynamicImport"> + | IF_BABEL_7<"explicitResourceManagement"> + | "exportDefaultFrom" + | IF_BABEL_7<"exportNamespaceFrom"> + | "flow" + | "flowComments" + | "functionBind" + | "functionSent" + | "importMeta" + | "jsx" + | IF_BABEL_7<"jsonStrings"> + | IF_BABEL_7<"logicalAssignment"> + | IF_BABEL_7<"importAssertions"> + | IF_BABEL_7<"importReflection"> + | "moduleBlocks" + | IF_BABEL_7<"moduleStringNames"> + | IF_BABEL_7<"nullishCoalescingOperator"> + | IF_BABEL_7<"numericSeparator"> + | IF_BABEL_7<"objectRestSpread"> + | IF_BABEL_7<"optionalCatchBinding"> + | IF_BABEL_7<"optionalChaining"> + | "partialApplication" + | "placeholders" + | IF_BABEL_7<"privateIn"> + | IF_BABEL_7<"regexpUnicodeSets"> + | "sourcePhaseImports" + | "throwExpressions" + | IF_BABEL_7<"topLevelAwait"> + | "v8intrinsic" + | ParserPluginWithOptions[0]; + +type ParserPluginWithOptions = + | ["decorators", DecoratorsPluginOptions] + | ["discardBinding", { syntaxType: "void" }] + | ["estree", { classFeatures?: boolean }] + | IF_BABEL_7<["importAttributes", { deprecatedAssertSyntax: boolean }]> + | IF_BABEL_7<["moduleAttributes", { version: "may-2020" }]> + | ["optionalChainingAssign", { version: "2023-07" }] + | ["pipelineOperator", PipelineOperatorPluginOptions] + | ["recordAndTuple", RecordAndTuplePluginOptions] + | ["flow", FlowPluginOptions] + | ["typescript", TypeScriptPluginOptions]; + +type PluginConfig = Plugin$1 | ParserPluginWithOptions; + +interface DecoratorsPluginOptions { + decoratorsBeforeExport?: boolean; + allowCallParenthesized?: boolean; +} + +interface PipelineOperatorPluginOptions { + proposal: BABEL_8_BREAKING extends false + ? "minimal" | "fsharp" | "hack" | "smart" + : "fsharp" | "hack"; + topicToken?: "%" | "#" | "@@" | "^^" | "^"; +} + +interface RecordAndTuplePluginOptions { + syntaxType: "bar" | "hash"; +} + +type FlowPluginOptions = BABEL_8_BREAKING extends true + ? { + all?: boolean; + enums?: boolean; + } + : { + all?: boolean; + }; + +interface TypeScriptPluginOptions { + dts?: boolean; + disallowAmbiguousJSXLike?: boolean; +} + +type Plugin = PluginConfig; + +type SourceType = "script" | "commonjs" | "module" | "unambiguous"; +interface Options { + /** + * By default, import and export declarations can only appear at a program's top level. + * Setting this option to true allows them anywhere where a statement is allowed. + */ + allowImportExportEverywhere?: boolean; + /** + * By default, await use is not allowed outside of an async function. + * Set this to true to accept such code. + */ + allowAwaitOutsideFunction?: boolean; + /** + * By default, a return statement at the top level raises an error. + * Set this to true to accept such code. + */ + allowReturnOutsideFunction?: boolean; + /** + * By default, new.target use is not allowed outside of a function or class. + * Set this to true to accept such code. + */ + allowNewTargetOutsideFunction?: boolean; + /** + * By default, super calls are not allowed outside of a method. + * Set this to true to accept such code. + */ + allowSuperOutsideMethod?: boolean; + /** + * By default, exported identifiers must refer to a declared variable. + * Set this to true to allow export statements to reference undeclared variables. + */ + allowUndeclaredExports?: boolean; + /** + * By default, yield use is not allowed outside of a generator function. + * Set this to true to accept such code. + */ + allowYieldOutsideFunction?: boolean; + /** + * By default, Babel parser JavaScript code according to Annex B syntax. + * Set this to `false` to disable such behavior. + */ + annexB?: boolean; + /** + * By default, Babel attaches comments to adjacent AST nodes. + * When this option is set to false, comments are not attached. + * It can provide up to 30% performance improvement when the input code has many comments. + * @babel/eslint-parser will set it for you. + * It is not recommended to use attachComment: false with Babel transform, + * as doing so removes all the comments in output code, and renders annotations such as + * /* istanbul ignore next *\/ nonfunctional. + */ + attachComment?: boolean; + /** + * By default, Babel always throws an error when it finds some invalid code. + * When this option is set to true, it will store the parsing error and + * try to continue parsing the invalid input file. + */ + errorRecovery?: boolean; + /** + * Indicate the mode the code should be parsed in. + * Can be one of "script", "commonjs", "module", or "unambiguous". Defaults to "script". + * "unambiguous" will make @babel/parser attempt to guess, based on the presence + * of ES6 import or export statements. + * Files with ES6 imports and exports are considered "module" and are otherwise "script". + * + * Use "commonjs" to parse code that is intended to be run in a CommonJS environment such as Node.js. + */ + sourceType?: SourceType; + /** + * Correlate output AST nodes with their source filename. + * Useful when generating code and source maps from the ASTs of multiple input files. + */ + sourceFilename?: string; + /** + * By default, all source indexes start from 0. + * You can provide a start index to alternatively start with. + * Useful for integration with other source tools. + */ + startIndex?: number; + /** + * By default, the first line of code parsed is treated as line 1. + * You can provide a line number to alternatively start with. + * Useful for integration with other source tools. + */ + startLine?: number; + /** + * By default, the parsed code is treated as if it starts from line 1, column 0. + * You can provide a column number to alternatively start with. + * Useful for integration with other source tools. + */ + startColumn?: number; + /** + * Array containing the plugins that you want to enable. + */ + plugins?: Plugin[]; + /** + * Should the parser work in strict mode. + * Defaults to true if sourceType === 'module'. Otherwise, false. + */ + strictMode?: boolean; + /** + * Adds a ranges property to each node: [node.start, node.end] + */ + ranges?: boolean; + /** + * Adds all parsed tokens to a tokens property on the File node. + */ + tokens?: boolean; + /** + * By default, the parser adds information about parentheses by setting + * `extra.parenthesized` to `true` as needed. + * When this option is `true` the parser creates `ParenthesizedExpression` + * AST nodes instead of using the `extra` property. + */ + createParenthesizedExpressions?: boolean; + /** + * The default is false in Babel 7 and true in Babel 8 + * Set this to true to parse it as an `ImportExpression` node. + * Otherwise `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`. + */ + createImportExpressions?: boolean; +} + +type ParserOptions = Partial; +type ParseError = ParseError$1; +type ParseResult = Result & { + comments: File["comments"]; + errors: null | ParseError[]; + tokens?: File["tokens"]; +}; +/** + * Parse the provided code as an entire ECMAScript program. + */ +declare function parse(input: string, options?: ParserOptions): ParseResult; +declare function parseExpression(input: string, options?: ParserOptions): ParseResult; + +declare const tokTypes: { + // todo(flow->ts) real token type + [name: string]: any; +}; + +export { DecoratorsPluginOptions, FlowPluginOptions, ParseError, ParseResult, ParserOptions, PluginConfig as ParserPlugin, ParserPluginWithOptions, PipelineOperatorPluginOptions, RecordAndTuplePluginOptions, TypeScriptPluginOptions, parse, parseExpression, tokTypes }; diff --git a/backend/node_modules/@babel/types/LICENSE b/backend/node_modules/@babel/types/LICENSE new file mode 100644 index 00000000..f31575ec --- /dev/null +++ b/backend/node_modules/@babel/types/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/@babel/types/README.md b/backend/node_modules/@babel/types/README.md new file mode 100644 index 00000000..54c9f819 --- /dev/null +++ b/backend/node_modules/@babel/types/README.md @@ -0,0 +1,19 @@ +# @babel/types + +> Babel Types is a Lodash-esque utility library for AST nodes + +See our website [@babel/types](https://babeljs.io/docs/babel-types) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen) associated with this package. + +## Install + +Using npm: + +```sh +npm install --save-dev @babel/types +``` + +or using yarn: + +```sh +yarn add @babel/types --dev +``` diff --git a/backend/node_modules/@babel/types/package.json b/backend/node_modules/@babel/types/package.json new file mode 100644 index 00000000..0c971fa7 --- /dev/null +++ b/backend/node_modules/@babel/types/package.json @@ -0,0 +1,39 @@ +{ + "name": "@babel/types", + "version": "7.29.8", + "description": "Babel Types is a Lodash-esque utility library for AST nodes", + "author": "The Babel Team (https://babel.dev/team)", + "homepage": "https://babel.dev/docs/en/next/babel-types", + "bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/babel/babel.git", + "directory": "packages/babel-types" + }, + "main": "./lib/index.js", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "devDependencies": { + "@babel/generator": "^7.29.8", + "@babel/helper-fixtures": "^7.29.7", + "@babel/parser": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "type": "commonjs", + "types": "./lib/index-legacy.d.ts", + "typesVersions": { + ">=4.1": { + "lib/index-legacy.d.ts": [ + "lib/index.d.ts" + ] + } + } +} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/LICENSE b/backend/node_modules/@jridgewell/sourcemap-codec/LICENSE new file mode 100644 index 00000000..1f6ce94c --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/LICENSE @@ -0,0 +1,19 @@ +Copyright 2024 Justin Ridgewell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/README.md b/backend/node_modules/@jridgewell/sourcemap-codec/README.md new file mode 100644 index 00000000..b3e0708b --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/README.md @@ -0,0 +1,264 @@ +# @jridgewell/sourcemap-codec + +Encode/decode the `mappings` property of a [sourcemap](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit). + + +## Why? + +Sourcemaps are difficult to generate and manipulate, because the `mappings` property – the part that actually links the generated code back to the original source – is encoded using an obscure method called [Variable-length quantity](https://en.wikipedia.org/wiki/Variable-length_quantity). On top of that, each segment in the mapping contains offsets rather than absolute indices, which means that you can't look at a segment in isolation – you have to understand the whole sourcemap. + +This package makes the process slightly easier. + + +## Installation + +```bash +npm install @jridgewell/sourcemap-codec +``` + + +## Usage + +```js +import { encode, decode } from '@jridgewell/sourcemap-codec'; + +var decoded = decode( ';EAEEA,EAAE,EAAC,CAAE;ECQY,UACC' ); + +assert.deepEqual( decoded, [ + // the first line (of the generated code) has no mappings, + // as shown by the starting semi-colon (which separates lines) + [], + + // the second line contains four (comma-separated) segments + [ + // segments are encoded as you'd expect: + // [ generatedCodeColumn, sourceIndex, sourceCodeLine, sourceCodeColumn, nameIndex ] + + // i.e. the first segment begins at column 2, and maps back to the second column + // of the second line (both zero-based) of the 0th source, and uses the 0th + // name in the `map.names` array + [ 2, 0, 2, 2, 0 ], + + // the remaining segments are 4-length rather than 5-length, + // because they don't map a name + [ 4, 0, 2, 4 ], + [ 6, 0, 2, 5 ], + [ 7, 0, 2, 7 ] + ], + + // the final line contains two segments + [ + [ 2, 1, 10, 19 ], + [ 12, 1, 11, 20 ] + ] +]); + +var encoded = encode( decoded ); +assert.equal( encoded, ';EAEEA,EAAE,EAAC,CAAE;ECQY,UACC' ); +``` + +## Benchmarks + +``` +node v20.10.0 + +amp.js.map - 45120 segments + +Decode Memory Usage: +local code 5815135 bytes +@jridgewell/sourcemap-codec 1.4.15 5868160 bytes +sourcemap-codec 5492584 bytes +source-map-0.6.1 13569984 bytes +source-map-0.8.0 6390584 bytes +chrome dev tools 8011136 bytes +Smallest memory usage is sourcemap-codec + +Decode speed: +decode: local code x 492 ops/sec ±1.22% (90 runs sampled) +decode: @jridgewell/sourcemap-codec 1.4.15 x 499 ops/sec ±1.16% (89 runs sampled) +decode: sourcemap-codec x 376 ops/sec ±1.66% (89 runs sampled) +decode: source-map-0.6.1 x 34.99 ops/sec ±0.94% (48 runs sampled) +decode: source-map-0.8.0 x 351 ops/sec ±0.07% (95 runs sampled) +chrome dev tools x 165 ops/sec ±0.91% (86 runs sampled) +Fastest is decode: @jridgewell/sourcemap-codec 1.4.15 + +Encode Memory Usage: +local code 444248 bytes +@jridgewell/sourcemap-codec 1.4.15 623024 bytes +sourcemap-codec 8696280 bytes +source-map-0.6.1 8745176 bytes +source-map-0.8.0 8736624 bytes +Smallest memory usage is local code + +Encode speed: +encode: local code x 796 ops/sec ±0.11% (97 runs sampled) +encode: @jridgewell/sourcemap-codec 1.4.15 x 795 ops/sec ±0.25% (98 runs sampled) +encode: sourcemap-codec x 231 ops/sec ±0.83% (86 runs sampled) +encode: source-map-0.6.1 x 166 ops/sec ±0.57% (86 runs sampled) +encode: source-map-0.8.0 x 203 ops/sec ±0.45% (88 runs sampled) +Fastest is encode: local code,encode: @jridgewell/sourcemap-codec 1.4.15 + + +*** + + +babel.min.js.map - 347793 segments + +Decode Memory Usage: +local code 35424960 bytes +@jridgewell/sourcemap-codec 1.4.15 35424696 bytes +sourcemap-codec 36033464 bytes +source-map-0.6.1 62253704 bytes +source-map-0.8.0 43843920 bytes +chrome dev tools 45111400 bytes +Smallest memory usage is @jridgewell/sourcemap-codec 1.4.15 + +Decode speed: +decode: local code x 38.18 ops/sec ±5.44% (52 runs sampled) +decode: @jridgewell/sourcemap-codec 1.4.15 x 38.36 ops/sec ±5.02% (52 runs sampled) +decode: sourcemap-codec x 34.05 ops/sec ±4.45% (47 runs sampled) +decode: source-map-0.6.1 x 4.31 ops/sec ±2.76% (15 runs sampled) +decode: source-map-0.8.0 x 55.60 ops/sec ±0.13% (73 runs sampled) +chrome dev tools x 16.94 ops/sec ±3.78% (46 runs sampled) +Fastest is decode: source-map-0.8.0 + +Encode Memory Usage: +local code 2606016 bytes +@jridgewell/sourcemap-codec 1.4.15 2626440 bytes +sourcemap-codec 21152576 bytes +source-map-0.6.1 25023928 bytes +source-map-0.8.0 25256448 bytes +Smallest memory usage is local code + +Encode speed: +encode: local code x 127 ops/sec ±0.18% (83 runs sampled) +encode: @jridgewell/sourcemap-codec 1.4.15 x 128 ops/sec ±0.26% (83 runs sampled) +encode: sourcemap-codec x 29.31 ops/sec ±2.55% (53 runs sampled) +encode: source-map-0.6.1 x 18.85 ops/sec ±3.19% (36 runs sampled) +encode: source-map-0.8.0 x 19.34 ops/sec ±1.97% (36 runs sampled) +Fastest is encode: @jridgewell/sourcemap-codec 1.4.15 + + +*** + + +preact.js.map - 1992 segments + +Decode Memory Usage: +local code 261696 bytes +@jridgewell/sourcemap-codec 1.4.15 244296 bytes +sourcemap-codec 302816 bytes +source-map-0.6.1 939176 bytes +source-map-0.8.0 336 bytes +chrome dev tools 587368 bytes +Smallest memory usage is source-map-0.8.0 + +Decode speed: +decode: local code x 17,782 ops/sec ±0.32% (97 runs sampled) +decode: @jridgewell/sourcemap-codec 1.4.15 x 17,863 ops/sec ±0.40% (100 runs sampled) +decode: sourcemap-codec x 12,453 ops/sec ±0.27% (101 runs sampled) +decode: source-map-0.6.1 x 1,288 ops/sec ±1.05% (96 runs sampled) +decode: source-map-0.8.0 x 9,289 ops/sec ±0.27% (101 runs sampled) +chrome dev tools x 4,769 ops/sec ±0.18% (100 runs sampled) +Fastest is decode: @jridgewell/sourcemap-codec 1.4.15 + +Encode Memory Usage: +local code 262944 bytes +@jridgewell/sourcemap-codec 1.4.15 25544 bytes +sourcemap-codec 323048 bytes +source-map-0.6.1 507808 bytes +source-map-0.8.0 507480 bytes +Smallest memory usage is @jridgewell/sourcemap-codec 1.4.15 + +Encode speed: +encode: local code x 24,207 ops/sec ±0.79% (95 runs sampled) +encode: @jridgewell/sourcemap-codec 1.4.15 x 24,288 ops/sec ±0.48% (96 runs sampled) +encode: sourcemap-codec x 6,761 ops/sec ±0.21% (100 runs sampled) +encode: source-map-0.6.1 x 5,374 ops/sec ±0.17% (99 runs sampled) +encode: source-map-0.8.0 x 5,633 ops/sec ±0.32% (99 runs sampled) +Fastest is encode: @jridgewell/sourcemap-codec 1.4.15,encode: local code + + +*** + + +react.js.map - 5726 segments + +Decode Memory Usage: +local code 678816 bytes +@jridgewell/sourcemap-codec 1.4.15 678816 bytes +sourcemap-codec 816400 bytes +source-map-0.6.1 2288864 bytes +source-map-0.8.0 721360 bytes +chrome dev tools 1012512 bytes +Smallest memory usage is local code + +Decode speed: +decode: local code x 6,178 ops/sec ±0.19% (98 runs sampled) +decode: @jridgewell/sourcemap-codec 1.4.15 x 6,261 ops/sec ±0.22% (100 runs sampled) +decode: sourcemap-codec x 4,472 ops/sec ±0.90% (99 runs sampled) +decode: source-map-0.6.1 x 449 ops/sec ±0.31% (95 runs sampled) +decode: source-map-0.8.0 x 3,219 ops/sec ±0.13% (100 runs sampled) +chrome dev tools x 1,743 ops/sec ±0.20% (99 runs sampled) +Fastest is decode: @jridgewell/sourcemap-codec 1.4.15 + +Encode Memory Usage: +local code 140960 bytes +@jridgewell/sourcemap-codec 1.4.15 159808 bytes +sourcemap-codec 969304 bytes +source-map-0.6.1 930520 bytes +source-map-0.8.0 930248 bytes +Smallest memory usage is local code + +Encode speed: +encode: local code x 8,013 ops/sec ±0.19% (100 runs sampled) +encode: @jridgewell/sourcemap-codec 1.4.15 x 7,989 ops/sec ±0.20% (101 runs sampled) +encode: sourcemap-codec x 2,472 ops/sec ±0.21% (99 runs sampled) +encode: source-map-0.6.1 x 2,200 ops/sec ±0.17% (99 runs sampled) +encode: source-map-0.8.0 x 2,220 ops/sec ±0.37% (99 runs sampled) +Fastest is encode: local code + + +*** + + +vscode.map - 2141001 segments + +Decode Memory Usage: +local code 198955264 bytes +@jridgewell/sourcemap-codec 1.4.15 199175352 bytes +sourcemap-codec 199102688 bytes +source-map-0.6.1 386323432 bytes +source-map-0.8.0 244116432 bytes +chrome dev tools 293734280 bytes +Smallest memory usage is local code + +Decode speed: +decode: local code x 3.90 ops/sec ±22.21% (15 runs sampled) +decode: @jridgewell/sourcemap-codec 1.4.15 x 3.95 ops/sec ±23.53% (15 runs sampled) +decode: sourcemap-codec x 3.82 ops/sec ±17.94% (14 runs sampled) +decode: source-map-0.6.1 x 0.61 ops/sec ±7.81% (6 runs sampled) +decode: source-map-0.8.0 x 9.54 ops/sec ±0.28% (28 runs sampled) +chrome dev tools x 2.18 ops/sec ±10.58% (10 runs sampled) +Fastest is decode: source-map-0.8.0 + +Encode Memory Usage: +local code 13509880 bytes +@jridgewell/sourcemap-codec 1.4.15 13537648 bytes +sourcemap-codec 32540104 bytes +source-map-0.6.1 127531040 bytes +source-map-0.8.0 127535312 bytes +Smallest memory usage is local code + +Encode speed: +encode: local code x 20.10 ops/sec ±0.19% (38 runs sampled) +encode: @jridgewell/sourcemap-codec 1.4.15 x 20.26 ops/sec ±0.32% (38 runs sampled) +encode: sourcemap-codec x 5.44 ops/sec ±1.64% (18 runs sampled) +encode: source-map-0.6.1 x 2.30 ops/sec ±4.79% (10 runs sampled) +encode: source-map-0.8.0 x 2.46 ops/sec ±6.53% (10 runs sampled) +Fastest is encode: @jridgewell/sourcemap-codec 1.4.15 +``` + +# License + +MIT diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/package.json b/backend/node_modules/@jridgewell/sourcemap-codec/package.json new file mode 100644 index 00000000..da551376 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/package.json @@ -0,0 +1,63 @@ +{ + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "description": "Encode/decode sourcemap mappings", + "keywords": [ + "sourcemap", + "vlq" + ], + "main": "dist/sourcemap-codec.umd.js", + "module": "dist/sourcemap-codec.mjs", + "types": "types/sourcemap-codec.d.cts", + "files": [ + "dist", + "src", + "types" + ], + "exports": { + ".": [ + { + "import": { + "types": "./types/sourcemap-codec.d.mts", + "default": "./dist/sourcemap-codec.mjs" + }, + "default": { + "types": "./types/sourcemap-codec.d.cts", + "default": "./dist/sourcemap-codec.umd.js" + } + }, + "./dist/sourcemap-codec.umd.js" + ], + "./package.json": "./package.json" + }, + "scripts": { + "benchmark": "run-s build:code benchmark:*", + "benchmark:install": "cd benchmark && npm install", + "benchmark:only": "node --expose-gc benchmark/index.js", + "build": "run-s -n build:code build:types", + "build:code": "node ../../esbuild.mjs sourcemap-codec.ts", + "build:types": "run-s build:types:force build:types:emit build:types:mts", + "build:types:force": "rimraf tsconfig.build.tsbuildinfo", + "build:types:emit": "tsc --project tsconfig.build.json", + "build:types:mts": "node ../../mts-types.mjs", + "clean": "run-s -n clean:code clean:types", + "clean:code": "tsc --build --clean tsconfig.build.json", + "clean:types": "rimraf dist types", + "test": "run-s -n test:types test:only test:format", + "test:format": "prettier --check '{src,test}/**/*.ts'", + "test:only": "mocha", + "test:types": "eslint '{src,test}/**/*.ts'", + "lint": "run-s -n lint:types lint:format", + "lint:format": "npm run test:format -- --write", + "lint:types": "npm run test:types -- --fix", + "prepublishOnly": "npm run-s -n build test" + }, + "homepage": "https://github.com/jridgewell/sourcemaps/tree/main/packages/sourcemap-codec", + "repository": { + "type": "git", + "url": "git+https://github.com/jridgewell/sourcemaps.git", + "directory": "packages/sourcemap-codec" + }, + "author": "Justin Ridgewell ", + "license": "MIT" +} diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts b/backend/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts new file mode 100644 index 00000000..d194c2f0 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts @@ -0,0 +1,345 @@ +import { StringReader, StringWriter } from './strings'; +import { comma, decodeInteger, encodeInteger, hasMoreVlq, semicolon } from './vlq'; + +const EMPTY: any[] = []; + +type Line = number; +type Column = number; +type Kind = number; +type Name = number; +type Var = number; +type SourcesIndex = number; +type ScopesIndex = number; + +type Mix = (A & O) | (B & O); + +export type OriginalScope = Mix< + [Line, Column, Line, Column, Kind], + [Line, Column, Line, Column, Kind, Name], + { vars: Var[] } +>; + +export type GeneratedRange = Mix< + [Line, Column, Line, Column], + [Line, Column, Line, Column, SourcesIndex, ScopesIndex], + { + callsite: CallSite | null; + bindings: Binding[]; + isScope: boolean; + } +>; +export type CallSite = [SourcesIndex, Line, Column]; +type Binding = BindingExpressionRange[]; +export type BindingExpressionRange = [Name] | [Name, Line, Column]; + +export function decodeOriginalScopes(input: string): OriginalScope[] { + const { length } = input; + const reader = new StringReader(input); + const scopes: OriginalScope[] = []; + const stack: OriginalScope[] = []; + let line = 0; + + for (; reader.pos < length; reader.pos++) { + line = decodeInteger(reader, line); + const column = decodeInteger(reader, 0); + + if (!hasMoreVlq(reader, length)) { + const last = stack.pop()!; + last[2] = line; + last[3] = column; + continue; + } + + const kind = decodeInteger(reader, 0); + const fields = decodeInteger(reader, 0); + const hasName = fields & 0b0001; + + const scope: OriginalScope = ( + hasName ? [line, column, 0, 0, kind, decodeInteger(reader, 0)] : [line, column, 0, 0, kind] + ) as OriginalScope; + + let vars: Var[] = EMPTY; + if (hasMoreVlq(reader, length)) { + vars = []; + do { + const varsIndex = decodeInteger(reader, 0); + vars.push(varsIndex); + } while (hasMoreVlq(reader, length)); + } + scope.vars = vars; + + scopes.push(scope); + stack.push(scope); + } + + return scopes; +} + +export function encodeOriginalScopes(scopes: OriginalScope[]): string { + const writer = new StringWriter(); + + for (let i = 0; i < scopes.length; ) { + i = _encodeOriginalScopes(scopes, i, writer, [0]); + } + + return writer.flush(); +} + +function _encodeOriginalScopes( + scopes: OriginalScope[], + index: number, + writer: StringWriter, + state: [ + number, // GenColumn + ], +): number { + const scope = scopes[index]; + const { 0: startLine, 1: startColumn, 2: endLine, 3: endColumn, 4: kind, vars } = scope; + + if (index > 0) writer.write(comma); + + state[0] = encodeInteger(writer, startLine, state[0]); + encodeInteger(writer, startColumn, 0); + encodeInteger(writer, kind, 0); + + const fields = scope.length === 6 ? 0b0001 : 0; + encodeInteger(writer, fields, 0); + if (scope.length === 6) encodeInteger(writer, scope[5], 0); + + for (const v of vars) { + encodeInteger(writer, v, 0); + } + + for (index++; index < scopes.length; ) { + const next = scopes[index]; + const { 0: l, 1: c } = next; + if (l > endLine || (l === endLine && c >= endColumn)) { + break; + } + index = _encodeOriginalScopes(scopes, index, writer, state); + } + + writer.write(comma); + state[0] = encodeInteger(writer, endLine, state[0]); + encodeInteger(writer, endColumn, 0); + + return index; +} + +export function decodeGeneratedRanges(input: string): GeneratedRange[] { + const { length } = input; + const reader = new StringReader(input); + const ranges: GeneratedRange[] = []; + const stack: GeneratedRange[] = []; + + let genLine = 0; + let definitionSourcesIndex = 0; + let definitionScopeIndex = 0; + let callsiteSourcesIndex = 0; + let callsiteLine = 0; + let callsiteColumn = 0; + let bindingLine = 0; + let bindingColumn = 0; + + do { + const semi = reader.indexOf(';'); + let genColumn = 0; + + for (; reader.pos < semi; reader.pos++) { + genColumn = decodeInteger(reader, genColumn); + + if (!hasMoreVlq(reader, semi)) { + const last = stack.pop()!; + last[2] = genLine; + last[3] = genColumn; + continue; + } + + const fields = decodeInteger(reader, 0); + const hasDefinition = fields & 0b0001; + const hasCallsite = fields & 0b0010; + const hasScope = fields & 0b0100; + + let callsite: CallSite | null = null; + let bindings: Binding[] = EMPTY; + let range: GeneratedRange; + if (hasDefinition) { + const defSourcesIndex = decodeInteger(reader, definitionSourcesIndex); + definitionScopeIndex = decodeInteger( + reader, + definitionSourcesIndex === defSourcesIndex ? definitionScopeIndex : 0, + ); + + definitionSourcesIndex = defSourcesIndex; + range = [genLine, genColumn, 0, 0, defSourcesIndex, definitionScopeIndex] as GeneratedRange; + } else { + range = [genLine, genColumn, 0, 0] as GeneratedRange; + } + + range.isScope = !!hasScope; + + if (hasCallsite) { + const prevCsi = callsiteSourcesIndex; + const prevLine = callsiteLine; + callsiteSourcesIndex = decodeInteger(reader, callsiteSourcesIndex); + const sameSource = prevCsi === callsiteSourcesIndex; + callsiteLine = decodeInteger(reader, sameSource ? callsiteLine : 0); + callsiteColumn = decodeInteger( + reader, + sameSource && prevLine === callsiteLine ? callsiteColumn : 0, + ); + + callsite = [callsiteSourcesIndex, callsiteLine, callsiteColumn]; + } + range.callsite = callsite; + + if (hasMoreVlq(reader, semi)) { + bindings = []; + do { + bindingLine = genLine; + bindingColumn = genColumn; + const expressionsCount = decodeInteger(reader, 0); + let expressionRanges: BindingExpressionRange[]; + if (expressionsCount < -1) { + expressionRanges = [[decodeInteger(reader, 0)]]; + for (let i = -1; i > expressionsCount; i--) { + const prevBl = bindingLine; + bindingLine = decodeInteger(reader, bindingLine); + bindingColumn = decodeInteger(reader, bindingLine === prevBl ? bindingColumn : 0); + const expression = decodeInteger(reader, 0); + expressionRanges.push([expression, bindingLine, bindingColumn]); + } + } else { + expressionRanges = [[expressionsCount]]; + } + bindings.push(expressionRanges); + } while (hasMoreVlq(reader, semi)); + } + range.bindings = bindings; + + ranges.push(range); + stack.push(range); + } + + genLine++; + reader.pos = semi + 1; + } while (reader.pos < length); + + return ranges; +} + +export function encodeGeneratedRanges(ranges: GeneratedRange[]): string { + if (ranges.length === 0) return ''; + + const writer = new StringWriter(); + + for (let i = 0; i < ranges.length; ) { + i = _encodeGeneratedRanges(ranges, i, writer, [0, 0, 0, 0, 0, 0, 0]); + } + + return writer.flush(); +} + +function _encodeGeneratedRanges( + ranges: GeneratedRange[], + index: number, + writer: StringWriter, + state: [ + number, // GenLine + number, // GenColumn + number, // DefSourcesIndex + number, // DefScopesIndex + number, // CallSourcesIndex + number, // CallLine + number, // CallColumn + ], +): number { + const range = ranges[index]; + const { + 0: startLine, + 1: startColumn, + 2: endLine, + 3: endColumn, + isScope, + callsite, + bindings, + } = range; + + if (state[0] < startLine) { + catchupLine(writer, state[0], startLine); + state[0] = startLine; + state[1] = 0; + } else if (index > 0) { + writer.write(comma); + } + + state[1] = encodeInteger(writer, range[1], state[1]); + + const fields = + (range.length === 6 ? 0b0001 : 0) | (callsite ? 0b0010 : 0) | (isScope ? 0b0100 : 0); + encodeInteger(writer, fields, 0); + + if (range.length === 6) { + const { 4: sourcesIndex, 5: scopesIndex } = range; + if (sourcesIndex !== state[2]) { + state[3] = 0; + } + state[2] = encodeInteger(writer, sourcesIndex, state[2]); + state[3] = encodeInteger(writer, scopesIndex, state[3]); + } + + if (callsite) { + const { 0: sourcesIndex, 1: callLine, 2: callColumn } = range.callsite!; + if (sourcesIndex !== state[4]) { + state[5] = 0; + state[6] = 0; + } else if (callLine !== state[5]) { + state[6] = 0; + } + state[4] = encodeInteger(writer, sourcesIndex, state[4]); + state[5] = encodeInteger(writer, callLine, state[5]); + state[6] = encodeInteger(writer, callColumn, state[6]); + } + + if (bindings) { + for (const binding of bindings) { + if (binding.length > 1) encodeInteger(writer, -binding.length, 0); + const expression = binding[0][0]; + encodeInteger(writer, expression, 0); + let bindingStartLine = startLine; + let bindingStartColumn = startColumn; + for (let i = 1; i < binding.length; i++) { + const expRange = binding[i]; + bindingStartLine = encodeInteger(writer, expRange[1]!, bindingStartLine); + bindingStartColumn = encodeInteger(writer, expRange[2]!, bindingStartColumn); + encodeInteger(writer, expRange[0]!, 0); + } + } + } + + for (index++; index < ranges.length; ) { + const next = ranges[index]; + const { 0: l, 1: c } = next; + if (l > endLine || (l === endLine && c >= endColumn)) { + break; + } + index = _encodeGeneratedRanges(ranges, index, writer, state); + } + + if (state[0] < endLine) { + catchupLine(writer, state[0], endLine); + state[0] = endLine; + state[1] = 0; + } else { + writer.write(comma); + } + state[1] = encodeInteger(writer, endColumn, state[1]); + + return index; +} + +function catchupLine(writer: StringWriter, lastLine: number, line: number) { + do { + writer.write(semicolon); + } while (++lastLine < line); +} diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts b/backend/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts new file mode 100644 index 00000000..a81f894d --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts @@ -0,0 +1,111 @@ +import { comma, decodeInteger, encodeInteger, hasMoreVlq, semicolon } from './vlq'; +import { StringWriter, StringReader } from './strings'; + +export { + decodeOriginalScopes, + encodeOriginalScopes, + decodeGeneratedRanges, + encodeGeneratedRanges, +} from './scopes'; +export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes'; + +export type SourceMapSegment = + | [number] + | [number, number, number, number] + | [number, number, number, number, number]; +export type SourceMapLine = SourceMapSegment[]; +export type SourceMapMappings = SourceMapLine[]; + +export function decode(mappings: string): SourceMapMappings { + const { length } = mappings; + const reader = new StringReader(mappings); + const decoded: SourceMapMappings = []; + let genColumn = 0; + let sourcesIndex = 0; + let sourceLine = 0; + let sourceColumn = 0; + let namesIndex = 0; + + do { + const semi = reader.indexOf(';'); + const line: SourceMapLine = []; + let sorted = true; + let lastCol = 0; + genColumn = 0; + + while (reader.pos < semi) { + let seg: SourceMapSegment; + + genColumn = decodeInteger(reader, genColumn); + if (genColumn < lastCol) sorted = false; + lastCol = genColumn; + + if (hasMoreVlq(reader, semi)) { + sourcesIndex = decodeInteger(reader, sourcesIndex); + sourceLine = decodeInteger(reader, sourceLine); + sourceColumn = decodeInteger(reader, sourceColumn); + + if (hasMoreVlq(reader, semi)) { + namesIndex = decodeInteger(reader, namesIndex); + seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex]; + } else { + seg = [genColumn, sourcesIndex, sourceLine, sourceColumn]; + } + } else { + seg = [genColumn]; + } + + line.push(seg); + reader.pos++; + } + + if (!sorted) sort(line); + decoded.push(line); + reader.pos = semi + 1; + } while (reader.pos <= length); + + return decoded; +} + +function sort(line: SourceMapSegment[]) { + line.sort(sortComparator); +} + +function sortComparator(a: SourceMapSegment, b: SourceMapSegment): number { + return a[0] - b[0]; +} + +export function encode(decoded: SourceMapMappings): string; +export function encode(decoded: Readonly): string; +export function encode(decoded: Readonly): string { + const writer = new StringWriter(); + let sourcesIndex = 0; + let sourceLine = 0; + let sourceColumn = 0; + let namesIndex = 0; + + for (let i = 0; i < decoded.length; i++) { + const line = decoded[i]; + if (i > 0) writer.write(semicolon); + if (line.length === 0) continue; + + let genColumn = 0; + + for (let j = 0; j < line.length; j++) { + const segment = line[j]; + if (j > 0) writer.write(comma); + + genColumn = encodeInteger(writer, segment[0], genColumn); + + if (segment.length === 1) continue; + sourcesIndex = encodeInteger(writer, segment[1], sourcesIndex); + sourceLine = encodeInteger(writer, segment[2], sourceLine); + sourceColumn = encodeInteger(writer, segment[3], sourceColumn); + + if (segment.length === 4) continue; + namesIndex = encodeInteger(writer, segment[4], namesIndex); + } + } + + return writer.flush(); +} diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/src/strings.ts b/backend/node_modules/@jridgewell/sourcemap-codec/src/strings.ts new file mode 100644 index 00000000..d1619650 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/src/strings.ts @@ -0,0 +1,65 @@ +const bufLength = 1024 * 16; + +// Provide a fallback for older environments. +const td = + typeof TextDecoder !== 'undefined' + ? /* #__PURE__ */ new TextDecoder() + : typeof Buffer !== 'undefined' + ? { + decode(buf: Uint8Array): string { + const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); + return out.toString(); + }, + } + : { + decode(buf: Uint8Array): string { + let out = ''; + for (let i = 0; i < buf.length; i++) { + out += String.fromCharCode(buf[i]); + } + return out; + }, + }; + +export class StringWriter { + pos = 0; + private out = ''; + private buffer = new Uint8Array(bufLength); + + write(v: number): void { + const { buffer } = this; + buffer[this.pos++] = v; + if (this.pos === bufLength) { + this.out += td.decode(buffer); + this.pos = 0; + } + } + + flush(): string { + const { buffer, out, pos } = this; + return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out; + } +} + +export class StringReader { + pos = 0; + declare private buffer: string; + + constructor(buffer: string) { + this.buffer = buffer; + } + + next(): number { + return this.buffer.charCodeAt(this.pos++); + } + + peek(): number { + return this.buffer.charCodeAt(this.pos); + } + + indexOf(char: string): number { + const { buffer, pos } = this; + const idx = buffer.indexOf(char, pos); + return idx === -1 ? buffer.length : idx; + } +} diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts b/backend/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts new file mode 100644 index 00000000..a42c6815 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts @@ -0,0 +1,55 @@ +import type { StringReader, StringWriter } from './strings'; + +export const comma = ','.charCodeAt(0); +export const semicolon = ';'.charCodeAt(0); + +const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +const intToChar = new Uint8Array(64); // 64 possible chars. +const charToInt = new Uint8Array(128); // z is 122 in ASCII + +for (let i = 0; i < chars.length; i++) { + const c = chars.charCodeAt(i); + intToChar[i] = c; + charToInt[c] = i; +} + +export function decodeInteger(reader: StringReader, relative: number): number { + let value = 0; + let shift = 0; + let integer = 0; + + do { + const c = reader.next(); + integer = charToInt[c]; + value |= (integer & 31) << shift; + shift += 5; + } while (integer & 32); + + const shouldNegate = value & 1; + value >>>= 1; + + if (shouldNegate) { + value = -0x80000000 | -value; + } + + return relative + value; +} + +export function encodeInteger(builder: StringWriter, num: number, relative: number): number { + let delta = num - relative; + + delta = delta < 0 ? (-delta << 1) | 1 : delta << 1; + do { + let clamped = delta & 0b011111; + delta >>>= 5; + if (delta > 0) clamped |= 0b100000; + builder.write(intToChar[clamped]); + } while (delta > 0); + + return num; +} + +export function hasMoreVlq(reader: StringReader, max: number) { + if (reader.pos >= max) return false; + return reader.peek() !== comma; +} diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts new file mode 100644 index 00000000..c583c756 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts @@ -0,0 +1,50 @@ +type Line = number; +type Column = number; +type Kind = number; +type Name = number; +type Var = number; +type SourcesIndex = number; +type ScopesIndex = number; +type Mix = (A & O) | (B & O); +export type OriginalScope = Mix<[ + Line, + Column, + Line, + Column, + Kind +], [ + Line, + Column, + Line, + Column, + Kind, + Name +], { + vars: Var[]; +}>; +export type GeneratedRange = Mix<[ + Line, + Column, + Line, + Column +], [ + Line, + Column, + Line, + Column, + SourcesIndex, + ScopesIndex +], { + callsite: CallSite | null; + bindings: Binding[]; + isScope: boolean; +}>; +export type CallSite = [SourcesIndex, Line, Column]; +type Binding = BindingExpressionRange[]; +export type BindingExpressionRange = [Name] | [Name, Line, Column]; +export declare function decodeOriginalScopes(input: string): OriginalScope[]; +export declare function encodeOriginalScopes(scopes: OriginalScope[]): string; +export declare function decodeGeneratedRanges(input: string): GeneratedRange[]; +export declare function encodeGeneratedRanges(ranges: GeneratedRange[]): string; +export {}; +//# sourceMappingURL=scopes.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map new file mode 100644 index 00000000..630e6477 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map @@ -0,0 +1 @@ +{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"AAKA,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,MAAM,GAAG,MAAM,CAAC;AACrB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,GAAG,GAAG,MAAM,CAAC;AAClB,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,WAAW,GAAG,MAAM,CAAC;AAE1B,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtC,MAAM,MAAM,aAAa,GAAG,GAAG,CAC7B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;CAAC,EAClC;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,IAAI;CAAC,EACxC;IAAE,IAAI,EAAE,GAAG,EAAE,CAAA;CAAE,CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,GAAG,CAC9B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;CAAC,EAC5B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,YAAY;IAAE,WAAW;CAAC,EACvD;IACE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB,CACF,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpD,KAAK,OAAO,GAAG,sBAAsB,EAAE,CAAC;AACxC,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEnE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,CAyCnE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAQpE;AA2CD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAoGrE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAUtE"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts new file mode 100644 index 00000000..c583c756 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts @@ -0,0 +1,50 @@ +type Line = number; +type Column = number; +type Kind = number; +type Name = number; +type Var = number; +type SourcesIndex = number; +type ScopesIndex = number; +type Mix = (A & O) | (B & O); +export type OriginalScope = Mix<[ + Line, + Column, + Line, + Column, + Kind +], [ + Line, + Column, + Line, + Column, + Kind, + Name +], { + vars: Var[]; +}>; +export type GeneratedRange = Mix<[ + Line, + Column, + Line, + Column +], [ + Line, + Column, + Line, + Column, + SourcesIndex, + ScopesIndex +], { + callsite: CallSite | null; + bindings: Binding[]; + isScope: boolean; +}>; +export type CallSite = [SourcesIndex, Line, Column]; +type Binding = BindingExpressionRange[]; +export type BindingExpressionRange = [Name] | [Name, Line, Column]; +export declare function decodeOriginalScopes(input: string): OriginalScope[]; +export declare function encodeOriginalScopes(scopes: OriginalScope[]): string; +export declare function decodeGeneratedRanges(input: string): GeneratedRange[]; +export declare function encodeGeneratedRanges(ranges: GeneratedRange[]): string; +export {}; +//# sourceMappingURL=scopes.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map new file mode 100644 index 00000000..630e6477 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map @@ -0,0 +1 @@ +{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"AAKA,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,MAAM,GAAG,MAAM,CAAC;AACrB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,KAAK,GAAG,GAAG,MAAM,CAAC;AAClB,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,WAAW,GAAG,MAAM,CAAC;AAE1B,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtC,MAAM,MAAM,aAAa,GAAG,GAAG,CAC7B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;CAAC,EAClC;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,IAAI;CAAC,EACxC;IAAE,IAAI,EAAE,GAAG,EAAE,CAAA;CAAE,CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,GAAG,CAC9B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;CAAC,EAC5B;IAAC,IAAI;IAAE,MAAM;IAAE,IAAI;IAAE,MAAM;IAAE,YAAY;IAAE,WAAW;CAAC,EACvD;IACE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB,CACF,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpD,KAAK,OAAO,GAAG,sBAAsB,EAAE,CAAC;AACxC,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEnE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE,CAyCnE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAQpE;AA2CD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE,CAoGrE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAUtE"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts new file mode 100644 index 00000000..5f35e22f --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts @@ -0,0 +1,9 @@ +export { decodeOriginalScopes, encodeOriginalScopes, decodeGeneratedRanges, encodeGeneratedRanges, } from './scopes.cts'; +export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes.cts'; +export type SourceMapSegment = [number] | [number, number, number, number] | [number, number, number, number, number]; +export type SourceMapLine = SourceMapSegment[]; +export type SourceMapMappings = SourceMapLine[]; +export declare function decode(mappings: string): SourceMapMappings; +export declare function encode(decoded: SourceMapMappings): string; +export declare function encode(decoded: Readonly): string; +//# sourceMappingURL=sourcemap-codec.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map new file mode 100644 index 00000000..7123d520 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map @@ -0,0 +1 @@ +{"version":3,"file":"sourcemap-codec.d.ts","sourceRoot":"","sources":["../src/sourcemap-codec.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEhG,MAAM,MAAM,gBAAgB,GACxB,CAAC,MAAM,CAAC,GACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAChC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAC7C,MAAM,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;AAC/C,MAAM,MAAM,iBAAiB,GAAG,aAAa,EAAE,CAAC;AAEhD,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAiD1D;AAUD,wBAAgB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAAC;AAC3D,wBAAgB,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts new file mode 100644 index 00000000..199fb9f5 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts @@ -0,0 +1,9 @@ +export { decodeOriginalScopes, encodeOriginalScopes, decodeGeneratedRanges, encodeGeneratedRanges, } from './scopes.mts'; +export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes.mts'; +export type SourceMapSegment = [number] | [number, number, number, number] | [number, number, number, number, number]; +export type SourceMapLine = SourceMapSegment[]; +export type SourceMapMappings = SourceMapLine[]; +export declare function decode(mappings: string): SourceMapMappings; +export declare function encode(decoded: SourceMapMappings): string; +export declare function encode(decoded: Readonly): string; +//# sourceMappingURL=sourcemap-codec.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map new file mode 100644 index 00000000..7123d520 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map @@ -0,0 +1 @@ +{"version":3,"file":"sourcemap-codec.d.ts","sourceRoot":"","sources":["../src/sourcemap-codec.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEhG,MAAM,MAAM,gBAAgB,GACxB,CAAC,MAAM,CAAC,GACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAChC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAC7C,MAAM,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;AAC/C,MAAM,MAAM,iBAAiB,GAAG,aAAa,EAAE,CAAC;AAEhD,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAiD1D;AAUD,wBAAgB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAAC;AAC3D,wBAAgB,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts new file mode 100644 index 00000000..62faceb3 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts @@ -0,0 +1,16 @@ +export declare class StringWriter { + pos: number; + private out; + private buffer; + write(v: number): void; + flush(): string; +} +export declare class StringReader { + pos: number; + private buffer; + constructor(buffer: string); + next(): number; + peek(): number; + indexOf(char: string): number; +} +//# sourceMappingURL=strings.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map new file mode 100644 index 00000000..d3602da4 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map @@ -0,0 +1 @@ +{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAuBA,qBAAa,YAAY;IACvB,GAAG,SAAK;IACR,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,MAAM,CAA6B;IAE3C,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAStB,KAAK,IAAI,MAAM;CAIhB;AAED,qBAAa,YAAY;IACvB,GAAG,SAAK;IACR,QAAgB,MAAM,CAAS;gBAEnB,MAAM,EAAE,MAAM;IAI1B,IAAI,IAAI,MAAM;IAId,IAAI,IAAI,MAAM;IAId,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAK9B"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts new file mode 100644 index 00000000..62faceb3 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts @@ -0,0 +1,16 @@ +export declare class StringWriter { + pos: number; + private out; + private buffer; + write(v: number): void; + flush(): string; +} +export declare class StringReader { + pos: number; + private buffer; + constructor(buffer: string); + next(): number; + peek(): number; + indexOf(char: string): number; +} +//# sourceMappingURL=strings.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map new file mode 100644 index 00000000..d3602da4 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map @@ -0,0 +1 @@ +{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAuBA,qBAAa,YAAY;IACvB,GAAG,SAAK;IACR,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,MAAM,CAA6B;IAE3C,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAStB,KAAK,IAAI,MAAM;CAIhB;AAED,qBAAa,YAAY;IACvB,GAAG,SAAK;IACR,QAAgB,MAAM,CAAS;gBAEnB,MAAM,EAAE,MAAM;IAI1B,IAAI,IAAI,MAAM;IAId,IAAI,IAAI,MAAM;IAId,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAK9B"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts new file mode 100644 index 00000000..dbd6602d --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts @@ -0,0 +1,7 @@ +import type { StringReader, StringWriter } from './strings.cts'; +export declare const comma: number; +export declare const semicolon: number; +export declare function decodeInteger(reader: StringReader, relative: number): number; +export declare function encodeInteger(builder: StringWriter, num: number, relative: number): number; +export declare function hasMoreVlq(reader: StringReader, max: number): boolean; +//# sourceMappingURL=vlq.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map new file mode 100644 index 00000000..6fdc3569 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map @@ -0,0 +1 @@ +{"version":3,"file":"vlq.d.ts","sourceRoot":"","sources":["../src/vlq.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE5D,eAAO,MAAM,KAAK,QAAoB,CAAC;AACvC,eAAO,MAAM,SAAS,QAAoB,CAAC;AAY3C,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoB5E;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY1F;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,WAG3D"} \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts new file mode 100644 index 00000000..2c739bc9 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts @@ -0,0 +1,7 @@ +import type { StringReader, StringWriter } from './strings.mts'; +export declare const comma: number; +export declare const semicolon: number; +export declare function decodeInteger(reader: StringReader, relative: number): number; +export declare function encodeInteger(builder: StringWriter, num: number, relative: number): number; +export declare function hasMoreVlq(reader: StringReader, max: number): boolean; +//# sourceMappingURL=vlq.d.ts.map \ No newline at end of file diff --git a/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map new file mode 100644 index 00000000..6fdc3569 --- /dev/null +++ b/backend/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map @@ -0,0 +1 @@ +{"version":3,"file":"vlq.d.ts","sourceRoot":"","sources":["../src/vlq.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE5D,eAAO,MAAM,KAAK,QAAoB,CAAC;AACvC,eAAO,MAAM,SAAS,QAAoB,CAAC;AAY3C,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoB5E;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY1F;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,WAG3D"} \ No newline at end of file diff --git a/backend/node_modules/@vue/compiler-core/LICENSE b/backend/node_modules/@vue/compiler-core/LICENSE new file mode 100644 index 00000000..15f1f7e7 --- /dev/null +++ b/backend/node_modules/@vue/compiler-core/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-present, Yuxi (Evan) You + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/node_modules/@vue/compiler-core/README.md b/backend/node_modules/@vue/compiler-core/README.md new file mode 100644 index 00000000..b43fc559 --- /dev/null +++ b/backend/node_modules/@vue/compiler-core/README.md @@ -0,0 +1 @@ +# @vue/compiler-core diff --git a/backend/node_modules/@vue/compiler-core/index.js b/backend/node_modules/@vue/compiler-core/index.js new file mode 100644 index 00000000..d3fc54f4 --- /dev/null +++ b/backend/node_modules/@vue/compiler-core/index.js @@ -0,0 +1,7 @@ +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./dist/compiler-core.cjs.prod.js') +} else { + module.exports = require('./dist/compiler-core.cjs.js') +} diff --git a/backend/node_modules/@vue/compiler-core/package.json b/backend/node_modules/@vue/compiler-core/package.json new file mode 100644 index 00000000..599dce80 --- /dev/null +++ b/backend/node_modules/@vue/compiler-core/package.json @@ -0,0 +1,58 @@ +{ + "name": "@vue/compiler-core", + "version": "3.5.40", + "description": "@vue/compiler-core", + "main": "index.js", + "module": "dist/compiler-core.esm-bundler.js", + "types": "dist/compiler-core.d.ts", + "files": [ + "index.js", + "dist" + ], + "exports": { + ".": { + "types": "./dist/compiler-core.d.ts", + "node": { + "production": "./dist/compiler-core.cjs.prod.js", + "development": "./dist/compiler-core.cjs.js", + "default": "./index.js" + }, + "module": "./dist/compiler-core.esm-bundler.js", + "import": "./dist/compiler-core.esm-bundler.js", + "require": "./index.js" + }, + "./*": "./*" + }, + "buildOptions": { + "name": "VueCompilerCore", + "compat": true, + "formats": [ + "esm-bundler", + "cjs" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vuejs/core.git", + "directory": "packages/compiler-core" + }, + "keywords": [ + "vue" + ], + "author": "Evan You", + "license": "MIT", + "bugs": { + "url": "https://github.com/vuejs/core/issues" + }, + "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme", + "dependencies": { + "@babel/parser": "^7.29.7", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1", + "@vue/shared": "3.5.40" + }, + "devDependencies": { + "@babel/types": "^7.29.7" + } +} \ No newline at end of file diff --git a/backend/node_modules/@vue/compiler-dom/LICENSE b/backend/node_modules/@vue/compiler-dom/LICENSE new file mode 100644 index 00000000..15f1f7e7 --- /dev/null +++ b/backend/node_modules/@vue/compiler-dom/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-present, Yuxi (Evan) You + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/node_modules/@vue/compiler-dom/README.md b/backend/node_modules/@vue/compiler-dom/README.md new file mode 100644 index 00000000..57748e66 --- /dev/null +++ b/backend/node_modules/@vue/compiler-dom/README.md @@ -0,0 +1 @@ +# @vue/compiler-dom diff --git a/backend/node_modules/@vue/compiler-dom/index.js b/backend/node_modules/@vue/compiler-dom/index.js new file mode 100644 index 00000000..b5f7a05e --- /dev/null +++ b/backend/node_modules/@vue/compiler-dom/index.js @@ -0,0 +1,7 @@ +'use strict' + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./dist/compiler-dom.cjs.prod.js') +} else { + module.exports = require('./dist/compiler-dom.cjs.js') +} diff --git a/backend/node_modules/@vue/compiler-dom/package.json b/backend/node_modules/@vue/compiler-dom/package.json new file mode 100644 index 00000000..cfd211fa --- /dev/null +++ b/backend/node_modules/@vue/compiler-dom/package.json @@ -0,0 +1,57 @@ +{ + "name": "@vue/compiler-dom", + "version": "3.5.40", + "description": "@vue/compiler-dom", + "main": "index.js", + "module": "dist/compiler-dom.esm-bundler.js", + "types": "dist/compiler-dom.d.ts", + "unpkg": "dist/compiler-dom.global.js", + "jsdelivr": "dist/compiler-dom.global.js", + "files": [ + "index.js", + "dist" + ], + "exports": { + ".": { + "types": "./dist/compiler-dom.d.ts", + "node": { + "production": "./dist/compiler-dom.cjs.prod.js", + "development": "./dist/compiler-dom.cjs.js", + "default": "./index.js" + }, + "module": "./dist/compiler-dom.esm-bundler.js", + "import": "./dist/compiler-dom.esm-bundler.js", + "require": "./index.js" + }, + "./*": "./*" + }, + "sideEffects": false, + "buildOptions": { + "name": "VueCompilerDOM", + "compat": true, + "formats": [ + "esm-bundler", + "esm-browser", + "cjs", + "global" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vuejs/core.git", + "directory": "packages/compiler-dom" + }, + "keywords": [ + "vue" + ], + "author": "Evan You", + "license": "MIT", + "bugs": { + "url": "https://github.com/vuejs/core/issues" + }, + "homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-dom#readme", + "dependencies": { + "@vue/shared": "3.5.40", + "@vue/compiler-core": "3.5.40" + } +} \ No newline at end of file diff --git a/backend/node_modules/@vue/compiler-sfc/LICENSE b/backend/node_modules/@vue/compiler-sfc/LICENSE new file mode 100644 index 00000000..15f1f7e7 --- /dev/null +++ b/backend/node_modules/@vue/compiler-sfc/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-present, Yuxi (Evan) You + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/node_modules/@vue/compiler-sfc/README.md b/backend/node_modules/@vue/compiler-sfc/README.md new file mode 100644 index 00000000..4e83b0d6 --- /dev/null +++ b/backend/node_modules/@vue/compiler-sfc/README.md @@ -0,0 +1,79 @@ +# @vue/compiler-sfc + +> Lower level utilities for compiling Vue Single File Components + +**Note: as of 3.2.13+, this package is included as a dependency of the main `vue` package and can be accessed as `vue/compiler-sfc`. This means you no longer need to explicitly install this package and ensure its version matches that of `vue`'s. Just use the main `vue/compiler-sfc` deep import instead.** + +This package contains lower level utilities that you can use if you are writing a plugin / transform for a bundler or module system that compiles Vue Single File Components (SFCs) into JavaScript. It is used in [vue-loader](https://github.com/vuejs/vue-loader) and [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue). + +## API + +The API is intentionally low-level due to the various considerations when integrating Vue SFCs in a build system: + +- Separate hot-module replacement (HMR) for script, template and styles + - template updates should not reset component state + - style updates should be performed without component re-render + +- Leveraging the tool's plugin system for pre-processor handling. e.g. `