@openstax/ts-utils 1.33.1 → 1.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cfnlintrc +2 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/workflows/ci.yml +36 -0
- package/.github/workflows/lint.yml +55 -0
- package/.nvmrc +1 -0
- package/.syncignore +4 -0
- package/.syncpackrc +18 -0
- package/CONTRIBUTING.md +96 -0
- package/LICENSE +661 -0
- package/Procfile +1 -0
- package/README.md +62 -90
- package/app.json +23 -0
- package/cspell.json +32 -0
- package/deploy/constants.env +21 -0
- package/deploy/deploy.bash +157 -0
- package/deploy/deployment-alt-region.cfn.yml +70 -0
- package/deploy/deployment.cfn.yml +650 -0
- package/deploy/destroy-deployment.bash +23 -0
- package/deploy/shared.cfn.yml +94 -0
- package/docs/lambda-build.md +35 -0
- package/package.json +12 -228
- package/packages/frontend/README.md +46 -0
- package/packages/frontend/package.json +101 -0
- package/packages/frontend/public/favicon.ico +0 -0
- package/packages/frontend/public/index.html +107 -0
- package/packages/frontend/public/maintenance.html +59 -0
- package/packages/frontend/public/manifest.json +15 -0
- package/packages/frontend/public/robots.txt +3 -0
- package/packages/frontend/script/make-certificate.bash +49 -0
- package/packages/frontend/script/server/cli.js +11 -0
- package/packages/frontend/script/server/index.js +47 -0
- package/packages/frontend/script/start.bash +22 -0
- package/packages/frontend/script/trust-localhost.bash +7 -0
- package/packages/frontend/src/auth/authProvider.ts +10 -0
- package/packages/frontend/src/auth/useAuth.ts +33 -0
- package/packages/frontend/src/components/Pagination.tsx +26 -0
- package/packages/frontend/src/configProvider/index.ts +53 -0
- package/packages/frontend/src/configProvider/use.ts +41 -0
- package/packages/frontend/src/core/context/services.spec.tsx +39 -0
- package/packages/frontend/src/core/context/services.tsx +16 -0
- package/packages/frontend/src/core/index.spec.ts +7 -0
- package/packages/frontend/src/core/index.ts +20 -0
- package/packages/frontend/src/core/services.tsx +14 -0
- package/packages/frontend/src/core/types.ts +3 -0
- package/packages/frontend/src/example/api.ts +28 -0
- package/packages/frontend/src/example/components/Layout.tsx +23 -0
- package/packages/frontend/src/example/screens/Home.spec.tsx +68 -0
- package/packages/frontend/src/example/screens/Home.tsx +78 -0
- package/packages/frontend/src/example/screens/ThingList.spec.tsx +60 -0
- package/packages/frontend/src/example/screens/ThingList.tsx +75 -0
- package/packages/frontend/src/example/screens/ThingView.spec.tsx +71 -0
- package/packages/frontend/src/example/screens/ThingView.tsx +47 -0
- package/packages/frontend/src/example/screens/index.ts +9 -0
- package/packages/frontend/src/index.css +159 -0
- package/packages/frontend/src/index.tsx +67 -0
- package/packages/frontend/src/react-app-env.d.ts +1 -0
- package/packages/frontend/src/routing/components/RouteLink.spec.tsx +55 -0
- package/packages/frontend/src/routing/components/RouteLink.tsx +35 -0
- package/packages/frontend/src/routing/middleware.ts +6 -0
- package/packages/frontend/src/routing/useQuery.ts +14 -0
- package/packages/frontend/src/setupProxy.js +19 -0
- package/packages/frontend/src/setupTests.ts +9 -0
- package/packages/frontend/src/tests/testServices.tsx +23 -0
- package/packages/frontend/tsconfig.json +27 -0
- package/packages/lambda/.eslintrc.js +64 -0
- package/packages/lambda/jest-global-setup.js +3 -0
- package/packages/lambda/jest-setup-after-env.js +1 -0
- package/packages/lambda/jest.config.js +31 -0
- package/packages/lambda/jest.resolver.js +17 -0
- package/packages/lambda/package.json +68 -0
- package/packages/lambda/script/build.bash +19 -0
- package/packages/lambda/script/bundle-functions.bash +10 -0
- package/packages/lambda/script/lambdaLocalProxy.js +16 -0
- package/packages/lambda/script/lambdaLocalProxy.spec.ts +147 -0
- package/packages/lambda/script/utils/getRouteData.ts +7 -0
- package/packages/lambda/script/utils/routeDataLoader.js +8 -0
- package/packages/lambda/script/utils/routeDataLoader.spec.ts +8 -0
- package/packages/lambda/src/functions/serviceApi/core/index.ts +7 -0
- package/packages/lambda/src/functions/serviceApi/core/request.spec.ts +38 -0
- package/packages/lambda/src/functions/serviceApi/core/request.ts +42 -0
- package/packages/lambda/src/functions/serviceApi/core/routes.spec.ts +7 -0
- package/packages/lambda/src/functions/serviceApi/core/routes.ts +10 -0
- package/packages/lambda/src/functions/serviceApi/core/services.ts +9 -0
- package/packages/lambda/src/functions/serviceApi/core/types.ts +13 -0
- package/packages/lambda/src/functions/serviceApi/entry/lambda/https-xray.ts +4 -0
- package/packages/lambda/src/functions/serviceApi/entry/lambda/index.spec.ts +48 -0
- package/packages/lambda/src/functions/serviceApi/entry/lambda/index.ts +58 -0
- package/packages/lambda/src/functions/serviceApi/entry/lambda/services.ts +36 -0
- package/packages/lambda/src/functions/serviceApi/entry/local.ts +71 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentSearchMiddleware.spec.ts +16 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentSearchMiddleware.ts +41 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentStoreMiddleware.spec.ts +78 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/example/documentStoreMiddleware.ts +70 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/example/routes.spec.ts +306 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/example/routes.ts +176 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/index.spec.ts +263 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/index.ts +134 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/authMiddleware.spec.ts +23 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/authMiddleware.ts +32 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/configMiddleware.spec.ts +10 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/configMiddleware.ts +7 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/frontendFileServerMiddleware.spec.ts +13 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/frontendFileServerMiddleware.ts +23 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/paginationMiddleware.spec.ts +9 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/paginationMiddleware.ts +9 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/searchMiddleware.spec.ts +12 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/searchMiddleware.ts +21 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/userRoleValidatorMiddleware.spec.ts +21 -0
- package/packages/lambda/src/functions/serviceApi/versions/v0/middleware/userRoleValidatorMiddleware.ts +18 -0
- package/packages/lambda/tsconfig.json +30 -0
- package/packages/lambda/webpack.config.js +97 -0
- package/packages/utils/.eslintrc.js +64 -0
- package/packages/utils/README.md +118 -0
- package/packages/utils/jest-global-setup.js +3 -0
- package/packages/utils/jest.config.js +25 -0
- package/packages/utils/jest.resolver.js +17 -0
- package/packages/utils/package.json +238 -0
- package/packages/utils/src/assertions/index.spec.ts +126 -0
- package/{dist/esm/assertions/index.js → packages/utils/src/assertions/index.ts} +64 -49
- package/packages/utils/src/aws/ssmService.ts +7 -0
- package/packages/utils/src/config/awsParameterConfig.ts +24 -0
- package/packages/utils/src/config/envConfig.ts +58 -0
- package/packages/utils/src/config/index.spec.ts +165 -0
- package/{dist/esm/config/index.d.ts → packages/utils/src/config/index.ts} +29 -13
- package/packages/utils/src/config/lambdaParameterConfig.ts +49 -0
- package/{dist/esm/config/replaceConfig.js → packages/utils/src/config/replaceConfig.ts} +16 -6
- package/packages/utils/src/config/resolveConfigValue.ts +10 -0
- package/packages/utils/src/errors/index.spec.ts +35 -0
- package/{dist/esm/errors/index.js → packages/utils/src/errors/index.ts} +57 -41
- package/packages/utils/src/fetch/fetchStatusRetry.spec.ts +197 -0
- package/packages/utils/src/fetch/fetchStatusRetry.ts +33 -0
- package/packages/utils/src/fetch/index.spec.ts +34 -0
- package/packages/utils/src/fetch/index.ts +87 -0
- package/packages/utils/src/guards/index.spec.ts +58 -0
- package/{dist/esm/guards/index.d.ts → packages/utils/src/guards/index.ts} +10 -7
- package/packages/utils/src/index.spec.ts +471 -0
- package/packages/utils/src/middleware/apiErrorHandler.spec.ts +65 -0
- package/packages/utils/src/middleware/apiErrorHandler.ts +67 -0
- package/packages/utils/src/middleware/apiSlowResponseMiddleware.spec.ts +184 -0
- package/packages/utils/src/middleware/apiSlowResponseMiddleware.ts +71 -0
- package/packages/utils/src/middleware/index.spec.ts +99 -0
- package/{dist/cjs/middleware/index.d.ts → packages/utils/src/middleware/index.ts} +53 -5
- package/packages/utils/src/middleware/lambdaCorsResponseMiddleware.spec.ts +103 -0
- package/packages/utils/src/middleware/lambdaCorsResponseMiddleware.ts +52 -0
- package/packages/utils/src/middleware/throwNotFoundMiddleware.spec.ts +20 -0
- package/packages/utils/src/middleware/throwNotFoundMiddleware.ts +11 -0
- package/packages/utils/src/misc/hashValue.ts +18 -0
- package/packages/utils/src/misc/helpers.ts +259 -0
- package/packages/utils/src/misc/merge.ts +48 -0
- package/{dist/esm/misc/partitionSequence.js → packages/utils/src/misc/partitionSequence.ts} +23 -15
- package/packages/utils/src/pagination/index.spec.ts +150 -0
- package/packages/utils/src/pagination/index.ts +117 -0
- package/{dist/esm/routing/helpers.js → packages/utils/src/routing/helpers.ts} +42 -30
- package/packages/utils/src/routing/index.spec.ts +553 -0
- package/packages/utils/src/routing/index.ts +424 -0
- package/packages/utils/src/routing/validators/zod.spec.ts +16 -0
- package/packages/utils/src/routing/validators/zod.ts +14 -0
- package/packages/utils/src/services/accountsGateway/README.md +3 -0
- package/packages/utils/src/services/accountsGateway/index.spec.ts +518 -0
- package/packages/utils/src/services/accountsGateway/index.ts +251 -0
- package/packages/utils/src/services/apiGateway/README.md +93 -0
- package/packages/utils/src/services/apiGateway/index.spec.ts +254 -0
- package/packages/utils/src/services/apiGateway/index.ts +189 -0
- package/packages/utils/src/services/authProvider/README.md +21 -0
- package/packages/utils/src/services/authProvider/browser.spec.ts +391 -0
- package/packages/utils/src/services/authProvider/browser.ts +209 -0
- package/packages/utils/src/services/authProvider/decryption.spec.ts +337 -0
- package/packages/utils/src/services/authProvider/decryption.ts +98 -0
- package/packages/utils/src/services/authProvider/index.ts +93 -0
- package/packages/utils/src/services/authProvider/stub.spec.ts +29 -0
- package/packages/utils/src/services/authProvider/subrequest.spec.ts +105 -0
- package/packages/utils/src/services/authProvider/subrequest.ts +68 -0
- package/packages/utils/src/services/authProvider/utils/decryptAndVerify.spec.ts +128 -0
- package/packages/utils/src/services/authProvider/utils/decryptAndVerify.ts +106 -0
- package/packages/utils/src/services/authProvider/utils/embeddedAuthProvider.spec.ts +26 -0
- package/packages/utils/src/services/authProvider/utils/embeddedAuthProvider.ts +57 -0
- package/packages/utils/src/services/authProvider/utils/userRoleValidator.spec.ts +135 -0
- package/packages/utils/src/services/authProvider/utils/userRoleValidator.ts +49 -0
- package/packages/utils/src/services/authProvider/utils/userSubrequest.spec.ts +26 -0
- package/packages/utils/src/services/authProvider/utils/userSubrequest.ts +10 -0
- package/packages/utils/src/services/documentStore/dynamoEncoding.ts +57 -0
- package/packages/utils/src/services/documentStore/fileSystemAssert.spec.ts +43 -0
- package/packages/utils/src/services/documentStore/fileSystemAssert.ts +10 -0
- package/{dist/cjs/services/documentStore/index.d.ts → packages/utils/src/services/documentStore/index.ts} +8 -8
- package/packages/utils/src/services/documentStore/unversioned/README.md +13 -0
- package/packages/utils/src/services/documentStore/unversioned/dynamodb.spec.ts +859 -0
- package/packages/utils/src/services/documentStore/unversioned/dynamodb.ts +243 -0
- package/packages/utils/src/services/documentStore/unversioned/file-system.spec.ts +629 -0
- package/packages/utils/src/services/documentStore/unversioned/file-system.ts +194 -0
- package/{dist/cjs/services/documentStore/unversioned/index.d.ts → packages/utils/src/services/documentStore/unversioned/index.ts} +2 -0
- package/packages/utils/src/services/documentStore/versioned/README.md +13 -0
- package/packages/utils/src/services/documentStore/versioned/dynamodb.spec.ts +376 -0
- package/packages/utils/src/services/documentStore/versioned/dynamodb.ts +167 -0
- package/packages/utils/src/services/documentStore/versioned/file-system.spec.ts +262 -0
- package/packages/utils/src/services/documentStore/versioned/file-system.ts +90 -0
- package/packages/utils/src/services/documentStore/versioned/index.ts +25 -0
- package/packages/utils/src/services/exercisesGateway/README.md +5 -0
- package/packages/utils/src/services/exercisesGateway/index.spec.ts +326 -0
- package/packages/utils/src/services/exercisesGateway/index.ts +163 -0
- package/packages/utils/src/services/fileServer/index.spec.ts +88 -0
- package/packages/utils/src/services/fileServer/index.ts +43 -0
- package/packages/utils/src/services/fileServer/localFileServer.spec.ts +182 -0
- package/packages/utils/src/services/fileServer/localFileServer.ts +159 -0
- package/packages/utils/src/services/fileServer/s3FileServer.spec.ts +266 -0
- package/packages/utils/src/services/fileServer/s3FileServer.ts +155 -0
- package/packages/utils/src/services/launchParams/index.spec.ts +366 -0
- package/packages/utils/src/services/launchParams/signer.ts +73 -0
- package/packages/utils/src/services/launchParams/verifier.ts +120 -0
- package/packages/utils/src/services/logger/console.spec.ts +29 -0
- package/{dist/esm/services/logger/console.js → packages/utils/src/services/logger/console.ts} +5 -2
- package/packages/utils/src/services/logger/index.spec.ts +65 -0
- package/{dist/esm/services/logger/index.d.ts → packages/utils/src/services/logger/index.ts} +23 -9
- package/packages/utils/src/services/lrsGateway/README.md +5 -0
- package/packages/utils/src/services/lrsGateway/addStatementDefaultFields.ts +22 -0
- package/packages/utils/src/services/lrsGateway/attempt-utils.spec.ts +847 -0
- package/packages/utils/src/services/lrsGateway/attempt-utils.ts +358 -0
- package/packages/utils/src/services/lrsGateway/file-system.spec.ts +363 -0
- package/packages/utils/src/services/lrsGateway/file-system.ts +165 -0
- package/packages/utils/src/services/lrsGateway/index.spec.ts +194 -0
- package/packages/utils/src/services/lrsGateway/index.ts +257 -0
- package/packages/utils/src/services/lrsGateway/xapiUtils.spec.ts +887 -0
- package/packages/utils/src/services/lrsGateway/xapiUtils.ts +262 -0
- package/packages/utils/src/services/postgresConnection/index.spec.ts +170 -0
- package/packages/utils/src/services/postgresConnection/index.ts +84 -0
- package/packages/utils/src/services/searchProvider/README.md +3 -0
- package/packages/utils/src/services/searchProvider/index.ts +59 -0
- package/packages/utils/src/services/searchProvider/memorySearchTheBadWay.spec.ts +526 -0
- package/packages/utils/src/services/searchProvider/memorySearchTheBadWay.ts +223 -0
- package/packages/utils/src/services/searchProvider/openSearch.spec.ts +926 -0
- package/packages/utils/src/services/searchProvider/openSearch.ts +195 -0
- package/{dist/esm/types.d.ts → packages/utils/src/types.ts} +34 -6
- package/packages/utils/tsconfig.json +31 -0
- package/packages/utils/tsconfig.without-specs.cjs.json +7 -0
- package/packages/utils/tsconfig.without-specs.esm.json +7 -0
- package/packages/utils/tsconfig.without-specs.json +6 -0
- package/scripts/build.bash +24 -0
- package/scripts/ci.bash +10 -0
- package/scripts/start.bash +29 -0
- package/dist/cjs/assertions/index.d.ts +0 -89
- package/dist/cjs/assertions/index.js +0 -157
- package/dist/cjs/aws/ssmService.d.ts +0 -5
- package/dist/cjs/aws/ssmService.js +0 -9
- package/dist/cjs/config/awsParameterConfig.d.ts +0 -10
- package/dist/cjs/config/awsParameterConfig.js +0 -26
- package/dist/cjs/config/envConfig.d.ts +0 -24
- package/dist/cjs/config/envConfig.js +0 -57
- package/dist/cjs/config/index.d.ts +0 -48
- package/dist/cjs/config/index.js +0 -35
- package/dist/cjs/config/lambdaParameterConfig.d.ts +0 -12
- package/dist/cjs/config/lambdaParameterConfig.js +0 -45
- package/dist/cjs/config/replaceConfig.d.ts +0 -14
- package/dist/cjs/config/replaceConfig.js +0 -22
- package/dist/cjs/config/resolveConfigValue.d.ts +0 -5
- package/dist/cjs/config/resolveConfigValue.js +0 -12
- package/dist/cjs/errors/index.d.ts +0 -88
- package/dist/cjs/errors/index.js +0 -123
- package/dist/cjs/fetch/fetchStatusRetry.d.ts +0 -8
- package/dist/cjs/fetch/fetchStatusRetry.js +0 -27
- package/dist/cjs/fetch/index.d.ts +0 -64
- package/dist/cjs/fetch/index.js +0 -55
- package/dist/cjs/guards/index.d.ts +0 -38
- package/dist/cjs/guards/index.js +0 -44
- package/dist/cjs/index.js +0 -20
- package/dist/cjs/middleware/apiErrorHandler.d.ts +0 -24
- package/dist/cjs/middleware/apiErrorHandler.js +0 -42
- package/dist/cjs/middleware/apiSlowResponseMiddleware.d.ts +0 -23
- package/dist/cjs/middleware/apiSlowResponseMiddleware.js +0 -54
- package/dist/cjs/middleware/index.js +0 -48
- package/dist/cjs/middleware/lambdaCorsResponseMiddleware.d.ts +0 -20
- package/dist/cjs/middleware/lambdaCorsResponseMiddleware.js +0 -44
- package/dist/cjs/middleware/throwNotFoundMiddleware.d.ts +0 -4
- package/dist/cjs/middleware/throwNotFoundMiddleware.js +0 -14
- package/dist/cjs/misc/hashValue.d.ts +0 -10
- package/dist/cjs/misc/hashValue.js +0 -17
- package/dist/cjs/misc/helpers.d.ts +0 -124
- package/dist/cjs/misc/helpers.js +0 -214
- package/dist/cjs/misc/merge.d.ts +0 -21
- package/dist/cjs/misc/merge.js +0 -45
- package/dist/cjs/misc/partitionSequence.d.ts +0 -35
- package/dist/cjs/misc/partitionSequence.js +0 -55
- package/dist/cjs/pagination/index.d.ts +0 -91
- package/dist/cjs/pagination/index.js +0 -83
- package/dist/cjs/routing/helpers.d.ts +0 -57
- package/dist/cjs/routing/helpers.js +0 -90
- package/dist/cjs/routing/index.d.ts +0 -290
- package/dist/cjs/routing/index.js +0 -295
- package/dist/cjs/routing/validators/zod.d.ts +0 -4
- package/dist/cjs/routing/validators/zod.js +0 -14
- package/dist/cjs/services/accountsGateway/index.d.ts +0 -92
- package/dist/cjs/services/accountsGateway/index.js +0 -138
- package/dist/cjs/services/apiGateway/index.d.ts +0 -68
- package/dist/cjs/services/apiGateway/index.js +0 -118
- package/dist/cjs/services/authProvider/browser.d.ts +0 -40
- package/dist/cjs/services/authProvider/browser.js +0 -155
- package/dist/cjs/services/authProvider/decryption.d.ts +0 -19
- package/dist/cjs/services/authProvider/decryption.js +0 -73
- package/dist/cjs/services/authProvider/index.d.ts +0 -63
- package/dist/cjs/services/authProvider/index.js +0 -34
- package/dist/cjs/services/authProvider/subrequest.d.ts +0 -13
- package/dist/cjs/services/authProvider/subrequest.js +0 -49
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.d.ts +0 -28
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +0 -91
- package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.d.ts +0 -26
- package/dist/cjs/services/authProvider/utils/embeddedAuthProvider.js +0 -47
- package/dist/cjs/services/authProvider/utils/userRoleValidator.d.ts +0 -13
- package/dist/cjs/services/authProvider/utils/userRoleValidator.js +0 -37
- package/dist/cjs/services/authProvider/utils/userSubrequest.d.ts +0 -3
- package/dist/cjs/services/authProvider/utils/userSubrequest.js +0 -13
- package/dist/cjs/services/documentStore/dynamoEncoding.d.ts +0 -10
- package/dist/cjs/services/documentStore/dynamoEncoding.js +0 -52
- package/dist/cjs/services/documentStore/fileSystemAssert.d.ts +0 -1
- package/dist/cjs/services/documentStore/fileSystemAssert.js +0 -14
- package/dist/cjs/services/documentStore/index.js +0 -2
- package/dist/cjs/services/documentStore/unversioned/dynamodb.d.ts +0 -31
- package/dist/cjs/services/documentStore/unversioned/dynamodb.js +0 -233
- package/dist/cjs/services/documentStore/unversioned/file-system.d.ts +0 -32
- package/dist/cjs/services/documentStore/unversioned/file-system.js +0 -214
- package/dist/cjs/services/documentStore/unversioned/index.js +0 -2
- package/dist/cjs/services/documentStore/versioned/dynamodb.d.ts +0 -25
- package/dist/cjs/services/documentStore/versioned/dynamodb.js +0 -143
- package/dist/cjs/services/documentStore/versioned/file-system.d.ts +0 -25
- package/dist/cjs/services/documentStore/versioned/file-system.js +0 -73
- package/dist/cjs/services/documentStore/versioned/index.d.ts +0 -17
- package/dist/cjs/services/documentStore/versioned/index.js +0 -2
- package/dist/cjs/services/exercisesGateway/index.d.ts +0 -67
- package/dist/cjs/services/exercisesGateway/index.js +0 -107
- package/dist/cjs/services/fileServer/index.d.ts +0 -30
- package/dist/cjs/services/fileServer/index.js +0 -19
- package/dist/cjs/services/fileServer/localFileServer.d.ts +0 -13
- package/dist/cjs/services/fileServer/localFileServer.js +0 -132
- package/dist/cjs/services/fileServer/s3FileServer.d.ts +0 -14
- package/dist/cjs/services/fileServer/s3FileServer.js +0 -132
- package/dist/cjs/services/launchParams/index.js +0 -7
- package/dist/cjs/services/launchParams/signer.d.ts +0 -23
- package/dist/cjs/services/launchParams/signer.js +0 -58
- package/dist/cjs/services/launchParams/verifier.d.ts +0 -21
- package/dist/cjs/services/launchParams/verifier.js +0 -129
- package/dist/cjs/services/logger/console.d.ts +0 -4
- package/dist/cjs/services/logger/console.js +0 -12
- package/dist/cjs/services/logger/index.d.ts +0 -39
- package/dist/cjs/services/logger/index.js +0 -31
- package/dist/cjs/services/lrsGateway/addStatementDefaultFields.d.ts +0 -5
- package/dist/cjs/services/lrsGateway/addStatementDefaultFields.js +0 -21
- package/dist/cjs/services/lrsGateway/attempt-utils.d.ts +0 -70
- package/dist/cjs/services/lrsGateway/attempt-utils.js +0 -258
- package/dist/cjs/services/lrsGateway/file-system.d.ts +0 -15
- package/dist/cjs/services/lrsGateway/file-system.js +0 -150
- package/dist/cjs/services/lrsGateway/index.d.ts +0 -122
- package/dist/cjs/services/lrsGateway/index.js +0 -148
- package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +0 -68
- package/dist/cjs/services/lrsGateway/xapiUtils.js +0 -109
- package/dist/cjs/services/postgresConnection/index.d.ts +0 -28
- package/dist/cjs/services/postgresConnection/index.js +0 -65
- package/dist/cjs/services/searchProvider/index.d.ts +0 -67
- package/dist/cjs/services/searchProvider/index.js +0 -2
- package/dist/cjs/services/searchProvider/memorySearchTheBadWay.d.ts +0 -20
- package/dist/cjs/services/searchProvider/memorySearchTheBadWay.js +0 -191
- package/dist/cjs/services/searchProvider/openSearch.d.ts +0 -28
- package/dist/cjs/services/searchProvider/openSearch.js +0 -154
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +0 -1
- package/dist/cjs/types.d.ts +0 -31
- package/dist/cjs/types.js +0 -2
- package/dist/esm/assertions/index.d.ts +0 -89
- package/dist/esm/aws/ssmService.d.ts +0 -5
- package/dist/esm/aws/ssmService.js +0 -6
- package/dist/esm/config/awsParameterConfig.d.ts +0 -10
- package/dist/esm/config/awsParameterConfig.js +0 -22
- package/dist/esm/config/envConfig.d.ts +0 -24
- package/dist/esm/config/envConfig.js +0 -53
- package/dist/esm/config/index.js +0 -17
- package/dist/esm/config/lambdaParameterConfig.d.ts +0 -12
- package/dist/esm/config/lambdaParameterConfig.js +0 -38
- package/dist/esm/config/replaceConfig.d.ts +0 -14
- package/dist/esm/config/resolveConfigValue.d.ts +0 -5
- package/dist/esm/config/resolveConfigValue.js +0 -8
- package/dist/esm/errors/index.d.ts +0 -88
- package/dist/esm/fetch/fetchStatusRetry.d.ts +0 -8
- package/dist/esm/fetch/fetchStatusRetry.js +0 -23
- package/dist/esm/fetch/index.d.ts +0 -64
- package/dist/esm/fetch/index.js +0 -46
- package/dist/esm/guards/index.js +0 -36
- package/dist/esm/index.d.ts +0 -4
- package/dist/esm/index.js +0 -4
- package/dist/esm/middleware/apiErrorHandler.d.ts +0 -24
- package/dist/esm/middleware/apiErrorHandler.js +0 -38
- package/dist/esm/middleware/apiSlowResponseMiddleware.d.ts +0 -23
- package/dist/esm/middleware/apiSlowResponseMiddleware.js +0 -50
- package/dist/esm/middleware/index.d.ts +0 -47
- package/dist/esm/middleware/index.js +0 -44
- package/dist/esm/middleware/lambdaCorsResponseMiddleware.d.ts +0 -20
- package/dist/esm/middleware/lambdaCorsResponseMiddleware.js +0 -40
- package/dist/esm/middleware/throwNotFoundMiddleware.d.ts +0 -4
- package/dist/esm/middleware/throwNotFoundMiddleware.js +0 -10
- package/dist/esm/misc/hashValue.d.ts +0 -10
- package/dist/esm/misc/hashValue.js +0 -13
- package/dist/esm/misc/helpers.d.ts +0 -124
- package/dist/esm/misc/helpers.js +0 -199
- package/dist/esm/misc/merge.d.ts +0 -21
- package/dist/esm/misc/merge.js +0 -40
- package/dist/esm/misc/partitionSequence.d.ts +0 -35
- package/dist/esm/pagination/index.d.ts +0 -91
- package/dist/esm/pagination/index.js +0 -77
- package/dist/esm/routing/helpers.d.ts +0 -57
- package/dist/esm/routing/index.d.ts +0 -290
- package/dist/esm/routing/index.js +0 -246
- package/dist/esm/routing/validators/zod.d.ts +0 -4
- package/dist/esm/routing/validators/zod.js +0 -10
- package/dist/esm/services/accountsGateway/index.d.ts +0 -92
- package/dist/esm/services/accountsGateway/index.js +0 -131
- package/dist/esm/services/apiGateway/index.d.ts +0 -68
- package/dist/esm/services/apiGateway/index.js +0 -77
- package/dist/esm/services/authProvider/browser.d.ts +0 -40
- package/dist/esm/services/authProvider/browser.js +0 -151
- package/dist/esm/services/authProvider/decryption.d.ts +0 -19
- package/dist/esm/services/authProvider/decryption.js +0 -69
- package/dist/esm/services/authProvider/index.d.ts +0 -63
- package/dist/esm/services/authProvider/index.js +0 -26
- package/dist/esm/services/authProvider/subrequest.d.ts +0 -13
- package/dist/esm/services/authProvider/subrequest.js +0 -45
- package/dist/esm/services/authProvider/utils/decryptAndVerify.d.ts +0 -28
- package/dist/esm/services/authProvider/utils/decryptAndVerify.js +0 -85
- package/dist/esm/services/authProvider/utils/embeddedAuthProvider.d.ts +0 -26
- package/dist/esm/services/authProvider/utils/embeddedAuthProvider.js +0 -40
- package/dist/esm/services/authProvider/utils/userRoleValidator.d.ts +0 -13
- package/dist/esm/services/authProvider/utils/userRoleValidator.js +0 -33
- package/dist/esm/services/authProvider/utils/userSubrequest.d.ts +0 -3
- package/dist/esm/services/authProvider/utils/userSubrequest.js +0 -6
- package/dist/esm/services/documentStore/dynamoEncoding.d.ts +0 -10
- package/dist/esm/services/documentStore/dynamoEncoding.js +0 -45
- package/dist/esm/services/documentStore/fileSystemAssert.d.ts +0 -1
- package/dist/esm/services/documentStore/fileSystemAssert.js +0 -10
- package/dist/esm/services/documentStore/index.d.ts +0 -14
- package/dist/esm/services/documentStore/index.js +0 -1
- package/dist/esm/services/documentStore/unversioned/dynamodb.d.ts +0 -31
- package/dist/esm/services/documentStore/unversioned/dynamodb.js +0 -226
- package/dist/esm/services/documentStore/unversioned/file-system.d.ts +0 -32
- package/dist/esm/services/documentStore/unversioned/file-system.js +0 -174
- package/dist/esm/services/documentStore/unversioned/index.d.ts +0 -2
- package/dist/esm/services/documentStore/unversioned/index.js +0 -1
- package/dist/esm/services/documentStore/versioned/dynamodb.d.ts +0 -25
- package/dist/esm/services/documentStore/versioned/dynamodb.js +0 -139
- package/dist/esm/services/documentStore/versioned/file-system.d.ts +0 -25
- package/dist/esm/services/documentStore/versioned/file-system.js +0 -69
- package/dist/esm/services/documentStore/versioned/index.d.ts +0 -17
- package/dist/esm/services/documentStore/versioned/index.js +0 -1
- package/dist/esm/services/exercisesGateway/index.d.ts +0 -67
- package/dist/esm/services/exercisesGateway/index.js +0 -70
- package/dist/esm/services/fileServer/index.d.ts +0 -30
- package/dist/esm/services/fileServer/index.js +0 -13
- package/dist/esm/services/fileServer/localFileServer.d.ts +0 -13
- package/dist/esm/services/fileServer/localFileServer.js +0 -125
- package/dist/esm/services/fileServer/s3FileServer.d.ts +0 -14
- package/dist/esm/services/fileServer/s3FileServer.js +0 -125
- package/dist/esm/services/launchParams/index.d.ts +0 -2
- package/dist/esm/services/launchParams/index.js +0 -2
- package/dist/esm/services/launchParams/signer.d.ts +0 -23
- package/dist/esm/services/launchParams/signer.js +0 -51
- package/dist/esm/services/launchParams/verifier.d.ts +0 -21
- package/dist/esm/services/launchParams/verifier.js +0 -92
- package/dist/esm/services/logger/console.d.ts +0 -4
- package/dist/esm/services/logger/index.js +0 -27
- package/dist/esm/services/lrsGateway/addStatementDefaultFields.d.ts +0 -5
- package/dist/esm/services/lrsGateway/addStatementDefaultFields.js +0 -14
- package/dist/esm/services/lrsGateway/attempt-utils.d.ts +0 -70
- package/dist/esm/services/lrsGateway/attempt-utils.js +0 -236
- package/dist/esm/services/lrsGateway/file-system.d.ts +0 -15
- package/dist/esm/services/lrsGateway/file-system.js +0 -110
- package/dist/esm/services/lrsGateway/index.d.ts +0 -122
- package/dist/esm/services/lrsGateway/index.js +0 -111
- package/dist/esm/services/lrsGateway/xapiUtils.d.ts +0 -68
- package/dist/esm/services/lrsGateway/xapiUtils.js +0 -99
- package/dist/esm/services/postgresConnection/index.d.ts +0 -28
- package/dist/esm/services/postgresConnection/index.js +0 -58
- package/dist/esm/services/searchProvider/index.d.ts +0 -67
- package/dist/esm/services/searchProvider/index.js +0 -1
- package/dist/esm/services/searchProvider/memorySearchTheBadWay.d.ts +0 -20
- package/dist/esm/services/searchProvider/memorySearchTheBadWay.js +0 -187
- package/dist/esm/services/searchProvider/openSearch.d.ts +0 -28
- package/dist/esm/services/searchProvider/openSearch.js +0 -150
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +0 -1
- package/dist/esm/types.js +0 -1
- package/script/bin/.init-params-script.bash.swp +0 -0
- /package/{script → packages/utils/script}/bin/copy-from-template.bash +0 -0
- /package/{script → packages/utils/script}/bin/delete-stack.bash +0 -0
- /package/{script → packages/utils/script}/bin/deploy.bash +0 -0
- /package/{script → packages/utils/script}/bin/destroy-deployment.bash +0 -0
- /package/{script → packages/utils/script}/bin/empty-bucket.bash +0 -0
- /package/{script → packages/utils/script}/bin/get-arg.bash +0 -0
- /package/{script → packages/utils/script}/bin/get-deployed-environments.bash +0 -0
- /package/{script → packages/utils/script}/bin/get-env-param.bash +0 -0
- /package/{script → packages/utils/script}/bin/get-kwarg.bash +0 -0
- /package/{script → packages/utils/script}/bin/get-stack-param.bash +0 -0
- /package/{script → packages/utils/script}/bin/has-flag.bash +0 -0
- /package/{script → packages/utils/script}/bin/init-constants-script.bash +0 -0
- /package/{script → packages/utils/script}/bin/init-params-script.bash +0 -0
- /package/{script → packages/utils/script}/bin/stack-exists.bash +0 -0
- /package/{script → packages/utils/script}/bin/update-utils.bash +0 -0
- /package/{script → packages/utils/script}/bin/upload-pager-duty-endpoints.bash +0 -0
- /package/{script → packages/utils/script}/bin/upload-params.bash +0 -0
- /package/{script → packages/utils/script}/bin/which.bash +0 -0
- /package/{script → packages/utils/script}/bin-entry.bash +0 -0
- /package/{script → packages/utils/script}/build.bash +0 -0
- /package/{dist/cjs/index.d.ts → packages/utils/src/index.ts} +0 -0
- /package/{dist/cjs/services/launchParams/index.d.ts → packages/utils/src/services/launchParams/index.ts} +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/* cspell:ignore presigner */
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { CopyObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
4
|
+
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
5
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
6
|
+
import { v4 as uuid } from 'uuid';
|
|
7
|
+
import { once } from '../..';
|
|
8
|
+
import { assertDefined } from '../../assertions';
|
|
9
|
+
import { ConfigProviderForConfig, resolveConfigValue } from '../../config';
|
|
10
|
+
import { ifDefined } from '../../guards';
|
|
11
|
+
import { FileServerAdapter, FileValue } from '.';
|
|
12
|
+
|
|
13
|
+
export type Config = {
|
|
14
|
+
bucketName: string;
|
|
15
|
+
bucketRegion: string;
|
|
16
|
+
// optional, if not provided attempting to get a public URL will throw an error
|
|
17
|
+
publicViewerDomain?: string;
|
|
18
|
+
};
|
|
19
|
+
interface Initializer<C> {
|
|
20
|
+
configSpace?: C;
|
|
21
|
+
getS3Client?: (...args: ConstructorParameters<typeof S3Client>) => S3Client;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const s3FileServer = <C extends string = 'deployed'>(initializer: Initializer<C>) => (configProvider: {[_key in C]: ConfigProviderForConfig<Config>}): FileServerAdapter => {
|
|
25
|
+
const config = configProvider[ifDefined(initializer.configSpace, 'deployed' as C)];
|
|
26
|
+
const bucketName = once(() => resolveConfigValue(config.bucketName));
|
|
27
|
+
const bucketRegion = once(() => resolveConfigValue(config.bucketRegion));
|
|
28
|
+
const publicViewerDomain = once(() => 'publicViewerDomain' in config && config.publicViewerDomain
|
|
29
|
+
? resolveConfigValue(config.publicViewerDomain)
|
|
30
|
+
: undefined
|
|
31
|
+
);
|
|
32
|
+
const s3Service = once(async () => {
|
|
33
|
+
const args = {apiVersion: '2012-08-10', region: await bucketRegion()};
|
|
34
|
+
return initializer.getS3Client?.(args) ?? new S3Client(args);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html
|
|
39
|
+
*/
|
|
40
|
+
const getSignedViewerUrl = async (source: FileValue) => {
|
|
41
|
+
const bucket = (await bucketName());
|
|
42
|
+
const command = new GetObjectCommand({Bucket: bucket, Key: source.path});
|
|
43
|
+
return getSignedUrl(await s3Service(), command, {
|
|
44
|
+
expiresIn: 3600, // 1 hour
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const getPublicViewerUrl = async (source: FileValue) => {
|
|
48
|
+
const host = assertDefined(await publicViewerDomain(),
|
|
49
|
+
new Error(`Tried to get public viewer URL for ${source.path} but no publicViewerDomain configured`)
|
|
50
|
+
);
|
|
51
|
+
return `https://${host}/${source.path}`;
|
|
52
|
+
};
|
|
53
|
+
const getFileContent = async (source: FileValue) => {
|
|
54
|
+
const bucket = await bucketName();
|
|
55
|
+
const command = new GetObjectCommand({Bucket: bucket, Key: source.path});
|
|
56
|
+
const response = await (await s3Service()).send(command);
|
|
57
|
+
|
|
58
|
+
return Buffer.from(await assertDefined(response.Body, new Error('Invalid Response from s3')).transformToByteArray());
|
|
59
|
+
};
|
|
60
|
+
const putFileContent = async (source: FileValue, content: string) => {
|
|
61
|
+
const bucket = await bucketName();
|
|
62
|
+
const command = new PutObjectCommand({
|
|
63
|
+
Bucket: bucket,
|
|
64
|
+
Key: source.path,
|
|
65
|
+
Body: content,
|
|
66
|
+
ContentType: source.mimeType,
|
|
67
|
+
});
|
|
68
|
+
await (await s3Service()).send(command);
|
|
69
|
+
return source;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/*
|
|
73
|
+
* https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_s3_presigned_post.html
|
|
74
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/HTTPPOSTExamples.html
|
|
75
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
|
|
76
|
+
*/
|
|
77
|
+
const getSignedFileUploadConfig = async() => {
|
|
78
|
+
const prefix = 'uploads/' + uuid();
|
|
79
|
+
const bucket = (await bucketName());
|
|
80
|
+
|
|
81
|
+
const Conditions = [
|
|
82
|
+
{ acl: 'private' },
|
|
83
|
+
{ bucket } as {bucket: string},
|
|
84
|
+
['starts-with', '$key', prefix] as ['starts-with', string, string]
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const defaultFields = {
|
|
88
|
+
acl: 'private',
|
|
89
|
+
};
|
|
90
|
+
const { url, fields } = await createPresignedPost(await s3Service(), {
|
|
91
|
+
Bucket: bucket,
|
|
92
|
+
Key: prefix + '/${filename}',
|
|
93
|
+
Conditions,
|
|
94
|
+
Fields: defaultFields,
|
|
95
|
+
Expires: 3600, // 1 hour
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
url, payload: fields
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const copyFileTo = async (source: FileValue, destinationPath: string) => {
|
|
104
|
+
const bucket = (await bucketName());
|
|
105
|
+
const destinationPathWithoutLeadingSlash = destinationPath.replace(/^\//, '');
|
|
106
|
+
|
|
107
|
+
const command = new CopyObjectCommand({
|
|
108
|
+
Bucket: bucket,
|
|
109
|
+
Key: destinationPathWithoutLeadingSlash,
|
|
110
|
+
CopySource: path.join(bucket, source.path),
|
|
111
|
+
});
|
|
112
|
+
await (await s3Service()).send(command);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
...source,
|
|
116
|
+
path: destinationPathWithoutLeadingSlash
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const copyFileToDirectory = async (source: FileValue, destination: string) => {
|
|
121
|
+
const destinationPath = path.join(destination, source.label);
|
|
122
|
+
return copyFileTo(source, destinationPath);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const isTemporaryUpload = (source: FileValue) => {
|
|
126
|
+
return source.path.indexOf('uploads/') === 0;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const getFileChecksum = async (source: FileValue) => {
|
|
130
|
+
const bucket = (await bucketName());
|
|
131
|
+
const command = new HeadObjectCommand({Bucket: bucket, Key: source.path});
|
|
132
|
+
const response = await (await s3Service()).send(command);
|
|
133
|
+
return assertDefined(response.ETag);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const filesEqual = async (sourceA: FileValue, sourceB: FileValue) => {
|
|
137
|
+
const [aSum, bSum] = await Promise.all(
|
|
138
|
+
[getFileChecksum(sourceA), getFileChecksum(sourceB)]
|
|
139
|
+
);
|
|
140
|
+
return aSum === bSum;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
getFileContent,
|
|
145
|
+
putFileContent,
|
|
146
|
+
getSignedViewerUrl,
|
|
147
|
+
getPublicViewerUrl,
|
|
148
|
+
getSignedFileUploadConfig,
|
|
149
|
+
copyFileTo,
|
|
150
|
+
copyFileToDirectory,
|
|
151
|
+
isTemporaryUpload,
|
|
152
|
+
getFileChecksum,
|
|
153
|
+
filesEqual,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/* spell-checker: ignore Xgub Jpbmci Blbn Imlzcy XVCIs */
|
|
2
|
+
import jwt from 'jsonwebtoken';
|
|
3
|
+
import { JwksClient } from 'jwks-rsa';
|
|
4
|
+
import ms from 'ms';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { SessionExpiredError } from '../../errors';
|
|
7
|
+
import { createLaunchSigner, createLaunchVerifier } from '.';
|
|
8
|
+
|
|
9
|
+
const alg = 'RS256' as const;
|
|
10
|
+
const expiresIn = '1m';
|
|
11
|
+
const iss = 'https://openstax.org';
|
|
12
|
+
// privateKey generated for testing only
|
|
13
|
+
const privateKey = `-----BEGIN PRIVATE KEY-----
|
|
14
|
+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDdyPcrfMDym/eS
|
|
15
|
+
jFEddXdbVh5PhvcikruXvzyJ/Ehocd0lgV/OMaSvdSqbmw1lJvC7NwuuhnslxwoD
|
|
16
|
+
0zHaUq+XR6dkaw09wPG4jzj76FByKyvFOKB8E+z3ARFDZkiRK6rdVTVnVwFG0TCl
|
|
17
|
+
Zwdb6FSX8DL8qOqRIDzxuXBmp02Y79srcarYpaHKwwOBt8Fjs8XSADj6OR+/abXP
|
|
18
|
+
5cAwtqM/P18efcSfGVqoraJMSNh7tJGTdOMiSrSY8n/F7xJytZf7YeVoppjIdjvG
|
|
19
|
+
TyHf3R3vg6xFJP63ISE1fdz8gDHR/Ix6kmWQN+t83/uYcxmcmA5CgszJXtKNxKAy
|
|
20
|
+
WtucJhj7AgMBAAECggEAWNKuov0ArWlneoq1xc2QssTHuOP9wBvyA3E0hrLCMQpB
|
|
21
|
+
Hk81FnhtU1Pib5VmhPdWfDQseoYjddrEGQoRMjXhWv34fVAeangGlvrNQJ4m8cJ2
|
|
22
|
+
cjMemvLD+Zy01EB3maRadvA0RiHjQgBvsh6UJOvkTUGGugfQgbTek5TH/mTMwA24
|
|
23
|
+
02DsewnNk5UnLkUsh5tDeJkq9UhI2sSdkLkUoUAyZYXdUF0RRxotPK9IIorA22Vu
|
|
24
|
+
Hw/WQ3Sr8RDyNDW0eVsmvh2jWj+vTKndqvryNqqHztpxeXh3BC9/flZjLZPgAFZ9
|
|
25
|
+
NVnB3nj2a3A9kqz/gu/jyK1DoC9e7a8xnetp1KxK+QKBgQD07n9yBmSh06kL71Sn
|
|
26
|
+
8YXD5BGKWCLW+w5GM2EqzwtwXy5TGP6A6ctQESQycYEer9P0PfOG8/T4eHe0xleB
|
|
27
|
+
OEVM/k+FfaxHuY3FU09gb0X04jKZnE3D9gNEzJfuejb12RLJkbFPGpzmrOSy+rgb
|
|
28
|
+
Kw9LUmppk3tkNE9aTGR+Wyj+5wKBgQDnzrHyorFuomebcih5HEXfozHg2XmLUwlW
|
|
29
|
+
ajF9c/VPKcLuzDsK0v10ZjLY30mR9i9tcwURb+YGMo3RxAxAXwcUvKzsi32fYpnn
|
|
30
|
+
oVn2vJeauasTL+NjKp/8SSlVQykyuVNHc5WD+CrfyMRuDQWitmZVmH0oX38xejML
|
|
31
|
+
OttDAaD2zQKBgQDieRySnGK0V3f5tyR53ZMoHFwzpWchjSYXty5jXOpgz0GQSI4b
|
|
32
|
+
ORJrSE5F7jnL3ByJvDbPVAfxL8LP/o2uyA8cMBHrhtajw2jZSj0dGAaBdh3/d01Y
|
|
33
|
+
osNG/D6Hna3wCPWcOADQ+fSWPsuAyt8tD9WmeeA3Gs1/S/cLsZNCWpw/hQKBgQDi
|
|
34
|
+
MbE2gEb/G/5ZksWnmE0ecpJjRBL0PuynpCvyrMo8mxWaCzO51jR39pm7Qfb6NO4G
|
|
35
|
+
sOvLHhOpDH+SpukFjzTLqWhb0amdc4uWmL2+pDyTQIWMzXfm9lryxTlN96El2+1F
|
|
36
|
+
laXaBrlfcPssIAzVv4KQF6JZWQY2c3WHicEW22oEKQKBgHt6n+j0ZO+Quq7k7FNr
|
|
37
|
+
6RAKDmAURh/+rPc3o357inI9u5oxktXvxI41ijHXTQRKrakAmiSYikPQOb/aOgjO
|
|
38
|
+
PNpj6gag4Srw7QkLI8ttl0yUCP155dxvnrK8Dd/t+oTsKTCR0EINSUUM8omDnHCL
|
|
39
|
+
5kaLB/QpHAKj0pF54unD8VzI
|
|
40
|
+
-----END PRIVATE KEY-----`;
|
|
41
|
+
|
|
42
|
+
const signerConfig = {
|
|
43
|
+
test: {
|
|
44
|
+
alg,
|
|
45
|
+
expiresIn,
|
|
46
|
+
iss,
|
|
47
|
+
privateKey,
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const launchSigner = createLaunchSigner({ configSpace: 'test' })(signerConfig);
|
|
52
|
+
|
|
53
|
+
describe('launchSigner', () => {
|
|
54
|
+
describe('jwks', () => {
|
|
55
|
+
it('outputs the public part of the key', async() => {
|
|
56
|
+
// cspell:disable
|
|
57
|
+
expect(await launchSigner.jwks()).toMatchInlineSnapshot(`
|
|
58
|
+
{
|
|
59
|
+
"keys": [
|
|
60
|
+
{
|
|
61
|
+
"e": "AQAB",
|
|
62
|
+
"kid": "wKgbD-KbvgA_GIKnggurrTGH_tGYVI8cDU01BuM6zC8",
|
|
63
|
+
"kty": "RSA",
|
|
64
|
+
"n": "3cj3K3zA8pv3koxRHXV3W1YeT4b3IpK7l788ifxIaHHdJYFfzjGkr3Uqm5sNZSbwuzcLroZ7JccKA9Mx2lKvl0enZGsNPcDxuI84--hQcisrxTigfBPs9wERQ2ZIkSuq3VU1Z1cBRtEwpWcHW-hUl_Ay_KjqkSA88blwZqdNmO_bK3Gq2KWhysMDgbfBY7PF0gA4-jkfv2m1z-XAMLajPz9fHn3EnxlaqK2iTEjYe7SRk3TjIkq0mPJ_xe8ScrWX-2HlaKaYyHY7xk8h390d74OsRST-tyEhNX3c_IAx0fyMepJlkDfrfN_7mHMZnJgOQoLMyV7SjcSgMlrbnCYY-w",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
}
|
|
68
|
+
`);
|
|
69
|
+
// cspell:enable
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('sign', () => {
|
|
74
|
+
let publicKey: string;
|
|
75
|
+
|
|
76
|
+
beforeEach(async() => {
|
|
77
|
+
const jwksUri = 'https://localhost/test';
|
|
78
|
+
const fetcher = (uri: string) => {
|
|
79
|
+
if (uri !== jwksUri) { throw new Error(`Unexpected URI: "${uri}"`); }
|
|
80
|
+
return launchSigner.jwks();
|
|
81
|
+
};
|
|
82
|
+
const jwksClient = new JwksClient({ fetcher, jwksUri });
|
|
83
|
+
const key = await jwksClient.getSigningKey();
|
|
84
|
+
publicKey = key.getPublicKey();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('returns a signed token', async() => {
|
|
88
|
+
const token = await launchSigner.sign({}, 'userId');
|
|
89
|
+
const { header, payload } = jwt.verify(token, publicKey, { complete: true });
|
|
90
|
+
if (typeof payload !== 'object') { throw new Error('Unexpected string payload'); }
|
|
91
|
+
|
|
92
|
+
expect(header.alg).toBe(alg);
|
|
93
|
+
expect(header.typ).toBe('JWT');
|
|
94
|
+
|
|
95
|
+
expect(payload.exp).toBeLessThanOrEqual(Math.floor(Date.now() / 1000) + 60);
|
|
96
|
+
expect(payload.sub).toBe('userId');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('can set token duration using a unix timestamp', async() => {
|
|
100
|
+
const maxExp = Math.floor(Date.now() / 1000) + 30;
|
|
101
|
+
const token = await launchSigner.sign({}, 'userId', maxExp);
|
|
102
|
+
const { payload } = jwt.verify(token, publicKey, { complete: true });
|
|
103
|
+
if (typeof payload !== 'object') { throw new Error('Unexpected string payload'); }
|
|
104
|
+
expect(payload.exp).toBeLessThanOrEqual(maxExp);
|
|
105
|
+
expect(payload.exp).toBeGreaterThanOrEqual(maxExp - 5);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('cannot exceed the configured max token duration', async() => {
|
|
109
|
+
const now = Math.floor(Date.now()/1000);
|
|
110
|
+
const maxExp = now + 86400; // 1 day
|
|
111
|
+
const actualMax = now + Math.floor(ms(expiresIn)/1000);
|
|
112
|
+
const token = await launchSigner.sign({}, 'userId', maxExp);
|
|
113
|
+
const { payload } = jwt.verify(token, publicKey, { complete: true });
|
|
114
|
+
if (typeof payload !== 'object') { throw new Error('Unexpected string payload'); }
|
|
115
|
+
expect(payload.exp).toBeLessThanOrEqual(actualMax);
|
|
116
|
+
expect(payload.exp).toBeGreaterThanOrEqual(actualMax - 5);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('throws when alg is invalid', () => {
|
|
120
|
+
const invalidLaunchSigner = createLaunchSigner({ configSpace: 'test' })({ test: { ...signerConfig.test, alg: 'none' } });
|
|
121
|
+
return expect(invalidLaunchSigner.sign({}, 'userId')).rejects.toThrow('"none" is not a valid algorithm');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('launchVerifier', () => {
|
|
127
|
+
const verifierConfig = {
|
|
128
|
+
test: {
|
|
129
|
+
trustedDomain: 'openstax.org',
|
|
130
|
+
bypassSignatureVerification: 'false',
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const fetcher = (uri: string) => {
|
|
135
|
+
if (uri !== new URL('/.well-known/jwks.json', iss).toString()) { throw new Error(`Unexpected URI: "${uri}"`); }
|
|
136
|
+
return launchSigner.jwks();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const services = {};
|
|
140
|
+
const launchVerifier = createLaunchVerifier({ configSpace: 'test', fetcher })(verifierConfig);
|
|
141
|
+
|
|
142
|
+
describe('with legacy format token', () => {
|
|
143
|
+
it('can verify string value tokens signed by the launchSigner reading value from .sub', async() => {
|
|
144
|
+
const token = await launchSigner.sign({}, 'testSuccessful=true');
|
|
145
|
+
expect((await launchVerifier(services).verify(token)).sub).toMatchInlineSnapshot('"testSuccessful=true"');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('can verify json value tokens signed by the launchSigner reading value from .sub', async() => {
|
|
149
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
150
|
+
expect((await launchVerifier(services).verify(token)).sub).toMatchInlineSnapshot('"{"testSuccessful":true}"');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('can verify json value tokens signed by the launchSigner reading value from token', async() => {
|
|
154
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
155
|
+
expect((await launchVerifier(services).verify(token)).testSuccessful).toMatchInlineSnapshot('true');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('can verify extended claim tokens signed by the launchSigner reading value from token', async() => {
|
|
159
|
+
const token = await launchSigner.sign({testSuccessful:true}, 'userId');
|
|
160
|
+
expect((await launchVerifier(services).verify(token)).testSuccessful).toMatchInlineSnapshot('true');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('with a validator', () => {
|
|
164
|
+
it('can use a default token', async() => {
|
|
165
|
+
const token = await launchSigner.sign({}, 'userId');
|
|
166
|
+
const validator = z.object({sub: z.string()});
|
|
167
|
+
expect((await launchVerifier(services, () => token).verify(validator.parse)).sub).toBe('userId');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('throws with no token provided', async() => {
|
|
171
|
+
const validator = z.object({sub: z.string()});
|
|
172
|
+
await expect(() => launchVerifier(services).verify(validator.parse)).rejects
|
|
173
|
+
.toThrowErrorMatchingInlineSnapshot('"Missing token for launch verification"')
|
|
174
|
+
;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('can verify string value tokens signed by the launchSigner reading value from .sub', async() => {
|
|
178
|
+
const token = await launchSigner.sign({}, 'testSuccessful=true');
|
|
179
|
+
const validator = z.object({sub: z.string()});
|
|
180
|
+
expect((await launchVerifier(services).verify(token, validator.parse)).sub).toMatchInlineSnapshot('"testSuccessful=true"');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('can verify json value tokens signed by the launchSigner reading value from .sub', async() => {
|
|
184
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
185
|
+
const validator = z.object({sub: z.string()});
|
|
186
|
+
expect((await launchVerifier(services).verify(token, validator.parse)).sub).toMatchInlineSnapshot('"{"testSuccessful":true}"');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('can verify json value tokens signed by the launchSigner reading extracted value', async() => {
|
|
190
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
191
|
+
const validator = z.object({testSuccessful: z.boolean()});
|
|
192
|
+
expect((await launchVerifier(services).verify(token, validator.parse)).testSuccessful).toMatchInlineSnapshot('true');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('can verify extended claim tokens signed by the launchSigner reading extracted value', async() => {
|
|
196
|
+
const token = await launchSigner.sign({testSuccessful:true}, 'userId');
|
|
197
|
+
const validator = z.object({testSuccessful: z.boolean()});
|
|
198
|
+
expect((await launchVerifier(services).verify(token, validator.parse)).testSuccessful).toMatchInlineSnapshot('true');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('strips unknown claims from result', async() => {
|
|
202
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true,"unknown":"value"}');
|
|
203
|
+
const validator = z.object({sub: z.string(), iss: z.string(), testSuccessful: z.boolean()});
|
|
204
|
+
expect(await launchVerifier(services).verify(token, validator.parse)).toMatchInlineSnapshot(`
|
|
205
|
+
{
|
|
206
|
+
"iss": "https://openstax.org",
|
|
207
|
+
"sub": "{"testSuccessful":true,"unknown":"value"}",
|
|
208
|
+
"testSuccessful": true,
|
|
209
|
+
}
|
|
210
|
+
`);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('throws on invalid token parameters', async() => {
|
|
214
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
215
|
+
const validator = z.object({testSuccessfullyBad: z.boolean()});
|
|
216
|
+
await expect(() => launchVerifier(services).verify(token, validator.parse)).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
217
|
+
"error in secret or public key callback: [
|
|
218
|
+
{
|
|
219
|
+
"expected": "boolean",
|
|
220
|
+
"code": "invalid_type",
|
|
221
|
+
"path": [
|
|
222
|
+
"testSuccessfullyBad"
|
|
223
|
+
],
|
|
224
|
+
"message": "Invalid input: expected boolean, received undefined"
|
|
225
|
+
}
|
|
226
|
+
]"
|
|
227
|
+
`);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('throws when the token is invalid', () => {
|
|
233
|
+
const token = 'R4ND0M';
|
|
234
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow('jwt malformed');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('throws when the token header is missing the iss claim', () => {
|
|
238
|
+
const token = jwt.sign({}, privateKey, { algorithm: alg, expiresIn, issuer: iss });
|
|
239
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow('error in secret or public key callback: JWT header missing iss claim');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('throws when the token is expired', () => {
|
|
243
|
+
const header = { alg, iss };
|
|
244
|
+
const token = jwt.sign({}, privateKey, { algorithm: alg, expiresIn: -1, header });
|
|
245
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow(SessionExpiredError);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('throws when the token iss is an invalid URL', () => {
|
|
249
|
+
const issuer = 'openstax.org';
|
|
250
|
+
const header = { alg, iss: issuer };
|
|
251
|
+
const token = jwt.sign({}, privateKey, { algorithm: alg, expiresIn, header, issuer });
|
|
252
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow('error in secret or public key callback: Invalid URL');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('throws when the token iss is untrusted', () => {
|
|
256
|
+
const issuer = 'https://example.com';
|
|
257
|
+
const header = { alg, iss: issuer };
|
|
258
|
+
const token = jwt.sign({}, privateKey, { algorithm: alg, expiresIn, header, issuer });
|
|
259
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow(
|
|
260
|
+
'error in secret or public key callback: Untrusted launch domain: "example.com"'
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('throws when the token payload is a string', () => {
|
|
265
|
+
const header = { alg, iss };
|
|
266
|
+
const token = jwt.sign('testSuccessful=true', privateKey, { header });
|
|
267
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow('received JWT token with unexpected non-JSON payload');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('throws when the token payload is missing the sub claim', () => {
|
|
271
|
+
const header = { alg, iss };
|
|
272
|
+
const token = jwt.sign({}, privateKey, { expiresIn, header, issuer: iss });
|
|
273
|
+
return expect(launchVerifier(services).verify(token)).rejects.toThrow('JWT payload missing sub claim');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe('with bypassSignatureVerification enabled', () => {
|
|
277
|
+
const bypassVerifierConfig = {
|
|
278
|
+
test: {
|
|
279
|
+
trustedDomain: 'openstax.org',
|
|
280
|
+
bypassSignatureVerification: 'true',
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const bypassLaunchVerifier = createLaunchVerifier({ configSpace: 'test', fetcher })(bypassVerifierConfig);
|
|
285
|
+
|
|
286
|
+
it('can verify tokens without signature verification', async() => {
|
|
287
|
+
const token = await launchSigner.sign({}, 'testSuccessful=true');
|
|
288
|
+
expect((await bypassLaunchVerifier(services).verify(token)).sub).toMatchInlineSnapshot('"testSuccessful=true"');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('can verify json value tokens reading value from token', async() => {
|
|
292
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
293
|
+
expect((await bypassLaunchVerifier(services).verify(token)).testSuccessful).toMatchInlineSnapshot('true');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('can verify extended claim tokens reading value from token', async() => {
|
|
297
|
+
const token = await launchSigner.sign({testSuccessful:true}, 'userId');
|
|
298
|
+
expect((await bypassLaunchVerifier(services).verify(token)).testSuccessful).toMatchInlineSnapshot('true');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('can verify tokens with invalid signatures when bypass is enabled', async() => {
|
|
302
|
+
// Create a malformed token that would normally fail signature verification
|
|
303
|
+
// but should succeed when bypass is enabled
|
|
304
|
+
const validToken = await launchSigner.sign({testSuccessful: true}, 'userId');
|
|
305
|
+
const [header, payload] = validToken.split('.');
|
|
306
|
+
const invalidToken = `${header}.${payload}.invalid_signature`;
|
|
307
|
+
|
|
308
|
+
expect((await bypassLaunchVerifier(services).verify(invalidToken)).testSuccessful).toBe(true);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('can verify expired tokens when bypass is enabled', async() => {
|
|
312
|
+
const expiredToken = jwt.sign({testSuccessful: true}, privateKey, {
|
|
313
|
+
algorithm: alg,
|
|
314
|
+
expiresIn: -1,
|
|
315
|
+
subject: 'userId',
|
|
316
|
+
issuer: iss
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect((await bypassLaunchVerifier(services).verify(expiredToken)).testSuccessful).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('still throws when the token payload is missing the sub claim', async() => {
|
|
323
|
+
const tokenWithoutSub = jwt.sign({testSuccessful: true}, privateKey, {
|
|
324
|
+
algorithm: alg,
|
|
325
|
+
expiresIn,
|
|
326
|
+
issuer: iss
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
await expect(bypassLaunchVerifier(services).verify(tokenWithoutSub)).rejects.toThrow('JWT payload missing sub claim');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('still throws when the token payload is not an object', async() => {
|
|
333
|
+
// Create a malformed token that would decode to a string
|
|
334
|
+
const malformedToken = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImlzcyI6Imh0dHBzOi8vb3BlbnN0YXgub3JnIn0.InRlc3RTdHJpbmci.signature';
|
|
335
|
+
|
|
336
|
+
await expect(bypassLaunchVerifier(services).verify(malformedToken)).rejects.toThrow('received JWT token with unexpected non-JSON payload');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('works with validator when bypass is enabled', async() => {
|
|
340
|
+
const token = await launchSigner.sign({}, '{"testSuccessful":true}');
|
|
341
|
+
const validator = z.object({testSuccessful: z.boolean()});
|
|
342
|
+
expect((await bypassLaunchVerifier(services).verify(token, validator.parse)).testSuccessful).toBe(true);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('with bypassSignatureVerification disabled', () => {
|
|
348
|
+
const normalVerifierConfig = {
|
|
349
|
+
test: {
|
|
350
|
+
trustedDomain: 'openstax.org',
|
|
351
|
+
bypassSignatureVerification: 'false',
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const normalLaunchVerifier = createLaunchVerifier({ configSpace: 'test', fetcher })(normalVerifierConfig);
|
|
356
|
+
|
|
357
|
+
it('still performs signature verification when bypass is explicitly disabled', async() => {
|
|
358
|
+
// Create a token with invalid signature that should fail verification when bypass is disabled
|
|
359
|
+
const validToken = await launchSigner.sign({testSuccessful: true}, 'userId');
|
|
360
|
+
const [header, payload] = validToken.split('.');
|
|
361
|
+
const invalidToken = `${header}.${payload}.invalid_signature`;
|
|
362
|
+
|
|
363
|
+
await expect(normalLaunchVerifier(services).verify(invalidToken)).rejects.toThrow();
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import ms from 'ms';
|
|
3
|
+
import { JWK } from 'node-jose';
|
|
4
|
+
import { once } from '../..';
|
|
5
|
+
import { ConfigProviderForConfig, resolveConfigValue } from '../../config';
|
|
6
|
+
import { ifDefined } from '../../guards';
|
|
7
|
+
import type { JsonCompatibleStruct } from '../../routing';
|
|
8
|
+
|
|
9
|
+
type Config = {
|
|
10
|
+
alg: string;
|
|
11
|
+
expiresIn: string;
|
|
12
|
+
iss: string;
|
|
13
|
+
privateKey: string;
|
|
14
|
+
};
|
|
15
|
+
interface Initializer<C> {
|
|
16
|
+
configSpace?: C;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// jsonwebtoken does not export this list
|
|
20
|
+
type SupportedAlgorithm = Exclude<jwt.Algorithm, 'none'>;
|
|
21
|
+
const SUPPORTED_ALGORITHMS: SupportedAlgorithm[] & ReadonlyArray<string> = [
|
|
22
|
+
'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512', 'PS256', 'PS384', 'PS512'
|
|
23
|
+
];
|
|
24
|
+
const assertAlg = (alg: string) => {
|
|
25
|
+
if ((SUPPORTED_ALGORITHMS).includes(alg)) {
|
|
26
|
+
return alg as SupportedAlgorithm;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
throw new Error(`"${alg}" is not a valid algorithm`);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a class that can sign launch params
|
|
34
|
+
*/
|
|
35
|
+
export const createLaunchSigner = <C extends string = 'launch'>({configSpace}: Initializer<C>) => (configProvider: {[_key in C]: ConfigProviderForConfig<Config>}) => {
|
|
36
|
+
const config = configProvider[ifDefined(configSpace, 'launch' as C)];
|
|
37
|
+
const getAlg = once(async() => assertAlg(await resolveConfigValue(config.alg)));
|
|
38
|
+
const getExpiresIn = once(() => resolveConfigValue(config.expiresIn));
|
|
39
|
+
const getIss = once(() => resolveConfigValue(config.iss));
|
|
40
|
+
const getPrivateKey = once(() => resolveConfigValue(config.privateKey));
|
|
41
|
+
const getKeyStore = once(async() => {
|
|
42
|
+
const keystore = JWK.createKeyStore();
|
|
43
|
+
await keystore.add(await getPrivateKey(), 'pem');
|
|
44
|
+
return keystore;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const jwks = async() => (await getKeyStore()).toJSON(false) as { keys: JWK.RawKey[] };
|
|
48
|
+
|
|
49
|
+
const getExpiresInWithMax = async(maxExp?: number | null) => {
|
|
50
|
+
const expiresIn = await getExpiresIn();
|
|
51
|
+
|
|
52
|
+
// The ms library used by jsonwebtoken can handle a value in seconds as well as a string like '1d'
|
|
53
|
+
if (!maxExp) { return expiresIn; }
|
|
54
|
+
|
|
55
|
+
// Convert both values to seconds for comparison
|
|
56
|
+
const expiresInSeconds = Math.floor(ms(expiresIn)/1000);
|
|
57
|
+
const maxExpSeconds = maxExp - Math.floor(Date.now()/1000);
|
|
58
|
+
return Math.min(expiresInSeconds, maxExpSeconds);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const sign = async(data: JsonCompatibleStruct, subject: string, maxExp?: number | null) => {
|
|
62
|
+
const alg = await getAlg();
|
|
63
|
+
// expiresIn can be a number of seconds or a string like '1h' or '1d'
|
|
64
|
+
const expiresIn = await getExpiresInWithMax(maxExp);
|
|
65
|
+
const iss = await getIss();
|
|
66
|
+
const header = { alg, iss };
|
|
67
|
+
return jwt.sign(data, await getPrivateKey(), { algorithm: alg, expiresIn, header, issuer: iss, subject });
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return { jwks, sign };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type LaunchSigner = ReturnType<ReturnType<typeof createLaunchSigner>>;
|