diff --git a/README.md b/README.md index 82749b9..ad4ad31 100644 --- a/README.md +++ b/README.md @@ -106,3 +106,26 @@ Coverage includes actual PostgreSQL writes, all field types, SQL views, CSV outp Forms portal endpoints, inclusive UTC date filtering, and full CSV exports are specified in [docs/api.md](docs/api.md). The dev and production `/lets-talk` migration, Kafka contract, seed command, and rollout order are in [docs/lets-talk.md](docs/lets-talk.md). + +## Shared authentication and bus client (v6 refresh) + +This branch uses `tc-core-library-js` **4.0.0-rc.1**. Use its named +`createJwtVerifier` / `createJwtAuthenticator`, `createM2MClient`, and +`createBusApiClient` exports for shared authentication and bus API publishing. +Service-specific roles, scopes, sessions and ownership checks remain in this service. +The former bus wrapper dependency and local JWT cryptography must not be reintroduced. + +RSA verification requires an exact configured issuer and audience (`JWT_AUDIENCE` +for client tokens, `AUTH0_AUDIENCE` for machine/API tokens, or this service's explicit +issuer-policy settings). HMAC secrets must be at least 32 bytes; no default secret +is supplied. Expiry is enforced. Machine identity comes from the shared verifier, +not the presence of scopes. Supply real development credentials through the service's +existing environment configuration before deployment. + +Bus publishers use `createBusApiClient(config).postEvent(event)` with the existing +event envelope and optional key. Configure `BUSAPI_URL` for v6 and the existing +OAuth credentials/token provider. The client uses bounded HTTPS requests and does +not retry POSTs automatically. Application retry/outbox behavior remains local. + +See the [core migration guide](https://github.com/topcoder-platform/tc-core-library-js/blob/v4.0.0-rc.1/MIGRATION.md) +for the API mapping, compatibility requirements, and rollout scope. diff --git a/package.json b/package.json index 80cb598..737c6d4 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,7 @@ "pg": "8.23.0", "reflect-metadata": "0.2.2", "rxjs": "7.8.2", - "tc-bus-api-wrapper": "github:topcoder-platform/tc-bus-api-wrapper#297a9c0adcdb97661257e7825bee9c3f5578b833", - "tc-core-library-js": "github:topcoder-platform/tc-core-library-js#323567bc50e433ae488b656f9f94e821ebaf3062" + "tc-core-library-js": "github:topcoder-platform/tc-core-library-js#v4.0.0-rc.1" }, "devDependencies": { "@eslint/js": "9.39.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60ca14a..d4fbaa1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - tc-core-library-js: github:topcoder-platform/tc-core-library-js#323567bc50e433ae488b656f9f94e821ebaf3062 - importers: .: @@ -62,12 +59,9 @@ importers: rxjs: specifier: 7.8.2 version: 7.8.2 - tc-bus-api-wrapper: - specifier: github:topcoder-platform/tc-bus-api-wrapper#297a9c0adcdb97661257e7825bee9c3f5578b833 - version: '@topcoder-platform/topcoder-bus-api-wrapper@https://codeload.github.com/topcoder-platform/tc-bus-api-wrapper/tar.gz/297a9c0adcdb97661257e7825bee9c3f5578b833(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)' tc-core-library-js: - specifier: github:topcoder-platform/tc-core-library-js#323567bc50e433ae488b656f9f94e821ebaf3062 - version: https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/323567bc50e433ae488b656f9f94e821ebaf3062(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0) + specifier: github:topcoder-platform/tc-core-library-js#v4.0.0-rc.1 + version: https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/6c326fea0abfccb5ae29dff2ac5bce01aabc1fa4(supports-color@7.2.0) devDependencies: '@eslint/js': specifier: 9.39.2 @@ -426,26 +420,6 @@ packages: '@noble/hashes': optional: true - '@hapi/address@5.1.1': - resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} - engines: {node: '>=14.0.0'} - - '@hapi/formula@3.0.2': - resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} - - '@hapi/hoek@11.0.7': - resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} - - '@hapi/pinpoint@2.0.1': - resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} - - '@hapi/tlds@1.1.7': - resolution: {integrity: sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==} - engines: {node: '>=14.0.0'} - - '@hapi/topo@6.0.2': - resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} - '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -994,10 +968,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@topcoder-platform/topcoder-bus-api-wrapper@https://codeload.github.com/topcoder-platform/tc-bus-api-wrapper/tar.gz/297a9c0adcdb97661257e7825bee9c3f5578b833': - resolution: {gitHosted: true, tarball: https://codeload.github.com/topcoder-platform/tc-bus-api-wrapper/tar.gz/297a9c0adcdb97661257e7825bee9c3f5578b833} - version: 1.2.0 - '@tsconfig/node10@1.0.13': resolution: {integrity: sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==} @@ -1314,13 +1284,6 @@ packages: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} - axios@0.30.3: - resolution: {integrity: sha512-5/tmEb6TmE/ax3mdXBc/Mi6YdPGxQsv+0p5YlciXWt3PHIn0VamqCXhRMtScnwY3lbgSXLneOuXAKUhgmSRpwg==} - - backoff@2.5.0: - resolution: {integrity: sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==} - engines: {node: '>= 0.6'} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1349,11 +1312,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - bunyan@1.8.15: - resolution: {integrity: sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==} - engines: {'0': node >=0.10.0} - hasBin: true - busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1407,9 +1365,6 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - codependency@2.1.0: - resolution: {integrity: sha512-JIdmYkE8Z6jwH1OUf4a5H5jk9YShPQkaYPUAiN+ktyChmPP77LGbeKrxWGPqdCnpTmt0hRIn8TXBVu01U3HDhg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1612,10 +1567,6 @@ packages: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} - dtrace-provider@0.8.8: - resolution: {integrity: sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==} - engines: {node: '>=0.10'} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1810,15 +1761,6 @@ packages: flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1872,10 +1814,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@6.0.4: - resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1972,10 +1910,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2022,10 +1956,6 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - joi@18.2.9: - resolution: {integrity: sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==} - engines: {node: '>= 20'} - jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} @@ -2192,9 +2122,6 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - millisecond@0.1.2: - resolution: {integrity: sha512-BJ8XtxY+woL+5TkP6uS6XvOArm0JVrX2otkgtWZseHpIax0oOOPW3cnwhOjRqbEJg7YRO/BDF7fO/PTWNT3T9Q==} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2226,13 +2153,6 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - - moment@2.31.0: - resolution: {integrity: sha512-0acOTfMiWOheYS4eoWb80yYMb/JLvVv9SHbs2PehaDzfUG0Bw855SKyk0IKTnPGa5+U2bmi3W68l1+sGLX/pvw==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2240,10 +2160,6 @@ packages: resolution: {integrity: sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==} engines: {node: '>= 10.16.0'} - mv@2.1.1: - resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} - engines: {node: '>=0.8.0'} - mysql2@3.15.3: resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} engines: {node: '>= 8.0'} @@ -2252,9 +2168,6 @@ packages: resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} engines: {node: '>=8.0.0'} - nan@2.29.0: - resolution: {integrity: sha512-GlGk3HIvitbvs+LT3g6XUP1kpirKNvmDFwF/bmo6XNWSb/eYEs/O4bfgIEIXCZ+lIOTS5xNwDvSGMw6FJdAhtA==} - nanoid@3.3.18: resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2263,10 +2176,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - ncp@2.0.0: - resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} - hasBin: true - negotiator@1.1.0: resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} engines: {node: '>=18'} @@ -2331,10 +2240,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2448,10 +2353,6 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} - precond@0.2.3: - resolution: {integrity: sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==} - engines: {node: '>= 0.6'} - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2488,9 +2389,6 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -2512,10 +2410,6 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - r7insight_node@2.1.1: - resolution: {integrity: sha512-xx0kgFxSHWY9aG1109uv4w2b+JLwHseSowOWo1bzCTDBpUk3er2rZdtQ90mAjUYbkh6Hus9DAwWvmHsX5pHaIQ==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -2552,9 +2446,6 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} - reconnect-core@1.3.0: - resolution: {integrity: sha512-+gLKwmyRf2tjl6bLR03DoeWELzyN6LW9Xgr3vh7NXHHwPi0JC0N2TwPyf90oUEBkCRcD+bgQ+s3HORoG3nwHDg==} - reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -2580,11 +2471,6 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - rimraf@2.4.5: - resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} @@ -2603,9 +2489,6 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-json-stringify@1.2.0: - resolution: {integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==} - safe-regex2@5.1.1: resolution: {integrity: sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==} hasBin: true @@ -2630,10 +2513,6 @@ packages: secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -2755,10 +2634,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tc-core-library-js@https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/323567bc50e433ae488b656f9f94e821ebaf3062: - resolution: {gitHosted: true, tarball: https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/323567bc50e433ae488b656f9f94e821ebaf3062} - version: 3.0.1 - engines: {node: '>= 14'} + tc-core-library-js@https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/6c326fea0abfccb5ae29dff2ac5bce01aabc1fa4: + resolution: {gitHosted: true, tarball: https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/6c326fea0abfccb5ae29dff2ac5bce01aabc1fa4} + version: 4.0.0-rc.1 + engines: {node: '>=22.13'} thread-stream@3.2.0: resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} @@ -3274,22 +3153,6 @@ snapshots: optionalDependencies: '@noble/hashes': 1.8.0 - '@hapi/address@5.1.1': - dependencies: - '@hapi/hoek': 11.0.7 - - '@hapi/formula@3.0.2': {} - - '@hapi/hoek@11.0.7': {} - - '@hapi/pinpoint@2.0.1': {} - - '@hapi/tlds@1.1.7': {} - - '@hapi/topo@6.0.2': - dependencies: - '@hapi/hoek': 11.0.7 - '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -3758,16 +3621,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@topcoder-platform/topcoder-bus-api-wrapper@https://codeload.github.com/topcoder-platform/tc-bus-api-wrapper/tar.gz/297a9c0adcdb97661257e7825bee9c3f5578b833(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0)': - dependencies: - joi: 18.2.9 - lodash: 4.18.1 - superagent: 10.3.0(supports-color@7.2.0) - tc-core-library-js: https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/323567bc50e433ae488b656f9f94e821ebaf3062(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0) - transitivePeerDependencies: - - debug - - supports-color - '@tsconfig/node10@1.0.13': {} '@tsconfig/node12@1.0.11': {} @@ -4176,18 +4029,6 @@ snapshots: aws-ssl-profiles@1.1.2: {} - axios@0.30.3(debug@4.4.3(supports-color@7.2.0)): - dependencies: - follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) - form-data: 4.0.6 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - backoff@2.5.0: - dependencies: - precond: 0.2.3 - balanced-match@1.0.2: {} better-result@2.10.0: {} @@ -4225,13 +4066,6 @@ snapshots: buffer-from@1.1.2: {} - bunyan@1.8.15: - optionalDependencies: - dtrace-provider: 0.8.8 - moment: 2.31.0 - mv: 2.1.1 - safe-json-stringify: 1.2.0 - busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -4288,10 +4122,6 @@ snapshots: classnames@2.5.1: {} - codependency@2.1.0: - dependencies: - semver: 5.7.2 - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -4460,11 +4290,6 @@ snapshots: dotenv@17.4.2: {} - dtrace-provider@0.8.8: - dependencies: - nan: 2.29.0 - optional: true - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4727,10 +4552,6 @@ snapshots: flatted@3.4.4: {} - follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)): - optionalDependencies: - debug: 4.4.3(supports-color@7.2.0) - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -4793,15 +4614,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@6.0.4: - dependencies: - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - optional: true - globals@14.0.0: {} globals@16.5.0: {} @@ -4881,12 +4693,6 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - optional: true - inherits@2.0.4: {} internmap@2.0.3: {} @@ -4915,16 +4721,6 @@ snapshots: jiti@2.7.0: {} - joi@18.2.9: - dependencies: - '@hapi/address': 5.1.1 - '@hapi/formula': 3.0.2 - '@hapi/hoek': 11.0.7 - '@hapi/pinpoint': 2.0.1 - '@hapi/tlds': 1.1.7 - '@hapi/topo': 6.0.2 - '@standard-schema/spec': 1.1.0 - jose@4.15.9: {} jose@5.10.0: {} @@ -5099,8 +4895,6 @@ snapshots: methods@1.1.2: {} - millisecond@0.1.2: {} - mime-db@1.52.0: {} mime-db@1.54.0: {} @@ -5125,14 +4919,6 @@ snapshots: minimist@1.2.8: {} - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - optional: true - - moment@2.31.0: - optional: true - ms@2.1.3: {} multer@2.2.0: @@ -5142,13 +4928,6 @@ snapshots: concat-stream: 2.0.0 type-is: 1.6.18 - mv@2.1.1: - dependencies: - mkdirp: 0.5.6 - ncp: 2.0.0 - rimraf: 2.4.5 - optional: true - mysql2@3.15.3: dependencies: aws-ssl-profiles: 1.1.2 @@ -5165,16 +4944,10 @@ snapshots: dependencies: lru.min: 1.1.5 - nan@2.29.0: - optional: true - nanoid@3.3.18: {} natural-compare@1.4.0: {} - ncp@2.0.0: - optional: true - negotiator@1.1.0: dependencies: content-type: 2.1.0 @@ -5234,9 +5007,6 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: - optional: true - path-key@3.1.1: {} path-to-regexp@6.3.0: {} @@ -5390,8 +5160,6 @@ snapshots: postgres@3.4.7: {} - precond@0.2.3: {} - prelude-ls@1.2.1: {} prettier@3.8.1: {} @@ -5428,8 +5196,6 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - proxy-from-env@1.1.0: {} - pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -5448,13 +5214,6 @@ snapshots: quick-format-unescaped@4.0.4: {} - r7insight_node@2.1.1: - dependencies: - codependency: 2.1.0 - json-stringify-safe: 5.0.1 - lodash: 4.18.1 - reconnect-core: 1.3.0 - range-parser@1.2.1: {} range-parser@1.3.0: {} @@ -5488,10 +5247,6 @@ snapshots: real-require@0.2.0: {} - reconnect-core@1.3.0: - dependencies: - backoff: 2.5.0 - reflect-metadata@0.2.2: {} remeda@2.33.4: {} @@ -5506,11 +5261,6 @@ snapshots: retry@0.12.0: {} - rimraf@2.4.5: - dependencies: - glob: 6.0.4 - optional: true - robust-predicates@3.0.3: {} rollup@4.63.1: @@ -5561,9 +5311,6 @@ snapshots: safe-buffer@5.2.1: {} - safe-json-stringify@1.2.0: - optional: true - safe-regex2@5.1.1: dependencies: ret: 0.5.0 @@ -5584,8 +5331,6 @@ snapshots: secure-json-parse@4.1.0: {} - semver@5.7.2: {} - semver@7.8.5: {} send@1.2.1(supports-color@7.2.0): @@ -5723,17 +5468,12 @@ snapshots: symbol-tree@3.2.4: {} - tc-core-library-js@https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/323567bc50e433ae488b656f9f94e821ebaf3062(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): + tc-core-library-js@https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/6c326fea0abfccb5ae29dff2ac5bce01aabc1fa4(supports-color@7.2.0): dependencies: - axios: 0.30.3(debug@4.4.3(supports-color@7.2.0)) - bunyan: 1.8.15 + '@types/jsonwebtoken': 9.0.10 jsonwebtoken: 9.0.3 jwks-rsa: 3.2.2(supports-color@7.2.0) - lodash: 4.18.1 - millisecond: 0.1.2 - r7insight_node: 2.1.1 transitivePeerDependencies: - - debug - supports-color thread-stream@3.2.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ef9b100..f33b7f3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,5 +7,3 @@ allowBuilds: dtrace-provider: false # The shared Bus API wrapper depends on Topcoder's Git-hosted core library. blockExoticSubdeps: false -overrides: - tc-core-library-js: github:topcoder-platform/tc-core-library-js#323567bc50e433ae488b656f9f94e821ebaf3062 diff --git a/src/auth.ts b/src/auth.ts index 6a528b2..745bac8 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -10,7 +10,7 @@ import { import { Reflector } from '@nestjs/core'; import type { Request } from 'express'; import jwt from 'jsonwebtoken'; -import { middleware, type AuthenticatorRequest } from 'tc-core-library-js'; +import { createJwtAuthenticator, type AuthenticatorRequest } from 'tc-core-library-js'; import { CONFIG, type AppConfig } from './config'; export type Permission = 'public' | 'manage' | 'report'; @@ -35,7 +35,7 @@ export interface ActorRequest extends Request { @Injectable() export class AuthGuard implements CanActivate { private readonly authenticator: ReturnType< - typeof middleware.jwtAuthenticator + typeof createJwtAuthenticator >; /** @@ -47,7 +47,9 @@ export class AuthGuard implements CanActivate { @Inject(CONFIG) private readonly config: AppConfig, private readonly reflector: Reflector, ) { - this.authenticator = middleware.jwtAuthenticator({ + this.authenticator = createJwtAuthenticator({ + AUTH0_AUDIENCE: config.machineAudience, + JWT_AUDIENCE: config.jwtAudience, AUTH_SECRET: config.authSecret, VALID_ISSUERS: JSON.stringify(config.issuers), }); @@ -101,7 +103,7 @@ export class AuthGuard implements CanActivate { * @throws Error for unsupported algorithms, invalid tokens or missing time claims. */ private async authenticate(token: string): Promise> { - // The shared verifier does not call back for unsupported algorithms. + // Reject unsupported algorithms before invoking the shared verifier. const decoded = jwt.decode(token, { complete: true }); if (!decoded || !['HS256', 'RS256'].includes(decoded.header.alg)) throw new Error('Unsupported token algorithm.'); @@ -148,11 +150,7 @@ export function normalizeActor( ',', ).map((r) => r.toLowerCase()); const scopes = stringList(claims.scope ?? claims.scopes, /\s+/); - const machine = - claims.gty === 'client-credentials' || - claims.isMachine === true || - (typeof claims.sub === 'string' && claims.sub.endsWith('@clients')) || - (scopes.length > 0 && roles.length === 0); + const machine = claims.isMachine === true; const userId = claims.userId ?? claims[`${namespace}userId`]; const memberId = !machine && diff --git a/src/config.ts b/src/config.ts index 855c898..bb47058 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,5 @@ import 'dotenv/config'; -import type { BusApiConfiguration } from 'tc-bus-api-wrapper'; +import type { BusApiConfiguration } from 'tc-core-library-js'; /** Runtime configuration validated once before Nest starts serving requests. */ export interface AppConfig { @@ -8,6 +8,8 @@ export interface AppConfig { port: number; origins: string[]; authSecret: string; + jwtAudience?: string; + machineAudience?: string; issuers: string[]; claimNamespace: string; throttleLimit: number; @@ -49,6 +51,8 @@ export function readConfig(env: NodeJS.ProcessEnv = process.env): AppConfig { port: integerSetting(env.PORT ?? '3000', 1, 65535), origins, authSecret, + jwtAudience: env.JWT_AUDIENCE, + machineAudience: env.AUTH0_AUDIENCE, issuers, claimNamespace: env.AUTH_CLAIM_NAMESPACE ?? 'https://topcoder.com/', throttleLimit: integerSetting(env.THROTTLE_LIMIT ?? '30', 1, 10000), diff --git a/src/integrations/event-bus.service.ts b/src/integrations/event-bus.service.ts index 058656d..c8a4d36 100644 --- a/src/integrations/event-bus.service.ts +++ b/src/integrations/event-bus.service.ts @@ -3,7 +3,7 @@ import { Injectable, ServiceUnavailableException, } from '@nestjs/common'; -import createBusApiClient, { type BusApiClient } from 'tc-bus-api-wrapper'; +import { createBusApiClient as createBusApiClient, type BusApiClient } from 'tc-core-library-js'; import { CONFIG, type AppConfig } from '../config'; import type { AnswerValue } from '../forms/validation'; diff --git a/src/types/tc-bus-api-wrapper.d.ts b/src/types/tc-bus-api-wrapper.d.ts deleted file mode 100644 index 612dcd3..0000000 --- a/src/types/tc-bus-api-wrapper.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -declare module 'tc-bus-api-wrapper' { - /** Configuration accepted by the shared Topcoder Bus API wrapper. */ - export interface BusApiConfiguration { - AUTH0_URL: string; - AUTH0_AUDIENCE: string; - AUTH0_CLIENT_ID: string; - AUTH0_CLIENT_SECRET: string; - BUSAPI_URL: string; - KAFKA_ERROR_TOPIC: string; - TOKEN_CACHE_TIME?: number; - AUTH0_PROXY_SERVER_URL?: string; - } - - /** Complete legacy-compatible event envelope published through Bus API. */ - export interface BusApiEvent { - topic: string; - originator: string; - timestamp: string; - 'mime-type': 'application/json'; - payload: T; - key?: string; - } - - /** Subset of the shared wrapper used by Forms API. */ - export interface BusApiClient { - postEvent(event: BusApiEvent): Promise; - } - - /** Creates an authenticated client whose base URL is the Topcoder API v6 base. */ - export default function createBusApiClient( - config: BusApiConfiguration, - ): BusApiClient; -} diff --git a/src/types/tc-core-library-js.d.ts b/src/types/tc-core-library-js.d.ts deleted file mode 100644 index 534e83b..0000000 --- a/src/types/tc-core-library-js.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -declare module 'tc-core-library-js' { - /** Minimal request shape populated by the shared Topcoder authenticator. */ - interface AuthenticatorRequest { - headers: { authorization: string }; - authUser?: Record; - } - - /** Response adapter converts the library's HTTP failures to Nest exceptions. */ - interface AuthenticatorResponse { - status(code: number): AuthenticatorResponse; - json(body?: unknown): void; - send(...args: unknown[]): void; - end(body?: unknown): void; - } - - /** Callback middleware exported by the shared Topcoder library. */ - type JwtAuthenticator = ( - request: AuthenticatorRequest, - response: AuthenticatorResponse, - next: (error?: unknown) => void, - ) => void; - - /** Shared JWT verifier and Topcoder claim-normalization entry point. */ - export const middleware: { - jwtAuthenticator(config: { - AUTH_SECRET: string; - VALID_ISSUERS: string; - }): JwtAuthenticator; - }; -} diff --git a/test/auth-rs256.spec.ts b/test/auth-rs256.spec.ts index 3a35e94..1fa27b0 100644 --- a/test/auth-rs256.spec.ts +++ b/test/auth-rs256.spec.ts @@ -81,6 +81,7 @@ beforeAll(async () => { 'https://api.topcoder-dev.com', ]), // Obsolete deployment settings must not keep rejecting Auth0 browser tokens. + JWT_AUDIENCE: 'BXWXUWnilVUPdN01t2Se29Tw2ZYNGZvH', AUTH_MODE: 'hs256', AUTH_AUDIENCE: 'forms-api', }), diff --git a/test/auth.spec.ts b/test/auth.spec.ts index 3c80ad0..731569f 100644 --- a/test/auth.spec.ts +++ b/test/auth.spec.ts @@ -101,7 +101,6 @@ describe('Topcoder bearer authentication', () => { { sub: 42, aud: 'forms-api' }, { gty: 'client-credentials' }, { isMachine: true }, - { scope: 'read:forms-submissions', roles: [] }, ])('rejects invalid or nonlegacy claims %#', async (claims) => { await expect(authenticate(claims)).rejects.toMatchObject({ status: 401 }); }); @@ -127,18 +126,14 @@ describe('Topcoder bearer authentication', () => { }, ); - it('keeps machine scope authorization separate from administrator roles', async () => { + it('rejects machine claims signed under the legacy audience-free human policy', async () => { const machine = { sub: 'client@clients', aud: 'forms-api', gty: 'client-credentials', + userId: undefined, roles: undefined, }; - await expect(authenticate(machine)).rejects.toMatchObject({ status: 403 }); - expect( - await authenticate({ ...machine, scope: 'read:forms-submissions' }), - ).toMatchObject({ - machine: true, - memberId: undefined, - }); + await expect(authenticate(machine)).rejects.toMatchObject({ status: 401 }); + await expect(authenticate({ ...machine, scope: 'read:forms-submissions' })).rejects.toMatchObject({ status: 401 }); }); }); diff --git a/test/event-bus.spec.ts b/test/event-bus.spec.ts index f0c6f44..8b0ca59 100644 --- a/test/event-bus.spec.ts +++ b/test/event-bus.spec.ts @@ -8,7 +8,7 @@ const { postEvent, createClient } = vi.hoisted(() => { const postEvent = vi.fn(); return { postEvent, createClient: vi.fn(() => ({ postEvent })) }; }); -vi.mock('tc-bus-api-wrapper', () => ({ default: createClient })); +vi.mock('tc-core-library-js', async (importOriginal) => ({ ...await importOriginal(), createBusApiClient: createClient })); const baseEnv = { DATABASE_URL: 'postgresql://localhost/forms', diff --git a/test/forms.integration.spec.ts b/test/forms.integration.spec.ts index 8409c29..adb3459 100644 --- a/test/forms.integration.spec.ts +++ b/test/forms.integration.spec.ts @@ -24,7 +24,7 @@ import { import { syncForm, type ManagedForm } from '../integrations/payload/forms'; const { postEvent } = vi.hoisted(() => ({ postEvent: vi.fn() })); -vi.mock('tc-bus-api-wrapper', () => ({ default: () => ({ postEvent }) })); +vi.mock('tc-core-library-js', async (importOriginal) => ({ ...await importOriginal(), createBusApiClient: () => ({ postEvent }) })); describe('forms API with real PostgreSQL', () => { let app: Awaited>;