feat(api): API tokens for authentication

Create and managed simple API tokens for access to the API from external
tools.
This commit is contained in:
Julian Tölle 2023-02-19 16:16:34 +01:00
parent eda89716ef
commit 8f7eebb806
15 changed files with 614 additions and 154 deletions

478
package-lock.json generated
View file

@ -50,6 +50,7 @@
"nestjs-pino": "3.1.2",
"nestjs-typeorm-paginate": "4.0.3",
"passport": "0.6.0",
"passport-http-bearer": "^1.0.1",
"passport-jwt": "4.0.1",
"passport-spotify": "2.0.0",
"pg": "8.8.0",
@ -70,6 +71,7 @@
"@types/jest": "29.2.5",
"@types/long": "4.0.2",
"@types/node": "18.11.19",
"@types/passport-http-bearer": "^1.0.37",
"@types/passport-jwt": "3.0.8",
"@types/supertest": "2.0.12",
"@typescript-eslint/eslint-plugin": "5.48.1",
@ -1479,12 +1481,12 @@
}
},
"node_modules/@jest/schemas": {
"version": "29.0.0",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz",
"integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
"integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
"dev": true,
"dependencies": {
"@sinclair/typebox": "^0.24.1"
"@sinclair/typebox": "^0.25.16"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@ -1583,12 +1585,12 @@
"dev": true
},
"node_modules/@jest/types": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz",
"integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.3.tgz",
"integrity": "sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==",
"dev": true,
"dependencies": {
"@jest/schemas": "^29.0.0",
"@jest/schemas": "^29.4.3",
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
@ -1677,13 +1679,13 @@
"devOptional": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
"version": "0.3.17",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
"@jridgewell/resolve-uri": "3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"node_modules/@narando/nest-axios-interceptor": {
@ -2709,17 +2711,17 @@
}
},
"node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.8.0.tgz",
"integrity": "sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.9.1.tgz",
"integrity": "sha512-6/qon6tw2I8ZaJnHAQUUn4BqhTbTNRS0WP8/bA0ynaX+Uzp/DDbd0NS0Cq6TMlh8+mrlsyqDE7mO50nmv2Yvlg==",
"dependencies": {
"@opentelemetry/semantic-conventions": "1.8.0"
"@opentelemetry/semantic-conventions": "1.9.1"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.0.0 <1.4.0"
"@opentelemetry/api": ">=1.0.0 <1.5.0"
}
},
"node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/instrumentation": {
@ -2739,9 +2741,9 @@
}
},
"node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/semantic-conventions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.8.0.tgz",
"integrity": "sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz",
"integrity": "sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg==",
"engines": {
"node": ">=14"
}
@ -2838,17 +2840,17 @@
}
},
"node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.8.0.tgz",
"integrity": "sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.9.1.tgz",
"integrity": "sha512-6/qon6tw2I8ZaJnHAQUUn4BqhTbTNRS0WP8/bA0ynaX+Uzp/DDbd0NS0Cq6TMlh8+mrlsyqDE7mO50nmv2Yvlg==",
"dependencies": {
"@opentelemetry/semantic-conventions": "1.8.0"
"@opentelemetry/semantic-conventions": "1.9.1"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@opentelemetry/api": ">=1.0.0 <1.4.0"
"@opentelemetry/api": ">=1.0.0 <1.5.0"
}
},
"node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/instrumentation": {
@ -2868,9 +2870,9 @@
}
},
"node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/semantic-conventions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.8.0.tgz",
"integrity": "sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz",
"integrity": "sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg==",
"engines": {
"node": ">=14"
}
@ -3436,9 +3438,9 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sinclair/typebox": {
"version": "0.24.43",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.43.tgz",
"integrity": "sha512-1orQTvtazZmsPeBroJjysvsOQCYV2yjWlebkSY38pl5vr2tdLjEJ+LoxITlGNZaH2RE19WlAwQMkH/7C14wLfw==",
"version": "0.25.23",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.23.tgz",
"integrity": "sha512-VEB8ygeP42CFLWyAJhN5OklpxUliqdNEUcXb4xZ/CINqtYGTjL5ukluKdKzQ0iWdUxyQ7B0539PAUhHKrCNWSQ==",
"dev": true
},
"node_modules/@sinonjs/commons": {
@ -3488,6 +3490,15 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"devOptional": true
},
"node_modules/@types/accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/babel__core": {
"version": "7.1.19",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz",
@ -3546,6 +3557,12 @@
"@types/node": "*"
}
},
"node_modules/@types/content-disposition": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz",
"integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==",
"dev": true
},
"node_modules/@types/cookie-parser": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz",
@ -3561,6 +3578,18 @@
"integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
"dev": true
},
"node_modules/@types/cookies": {
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz",
"integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/express": "*",
"@types/keygrip": "*",
"@types/node": "*"
}
},
"node_modules/@types/eslint": {
"version": "8.4.6",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
@ -3624,6 +3653,18 @@
"integrity": "sha512-oOMFT8vmCTFncsF1engrs04jatz8/Anwx3De9uxnOK4chgSEgWBvFtpSoJo8u3784JNO+ql5tzRR6phHoRnscQ==",
"dev": true
},
"node_modules/@types/http-assert": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz",
"integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==",
"dev": true
},
"node_modules/@types/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==",
"dev": true
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@ -3678,6 +3719,37 @@
"@types/node": "*"
}
},
"node_modules/@types/keygrip": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
"integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==",
"dev": true
},
"node_modules/@types/koa": {
"version": "2.13.5",
"resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.5.tgz",
"integrity": "sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA==",
"dev": true,
"dependencies": {
"@types/accepts": "*",
"@types/content-disposition": "*",
"@types/cookies": "*",
"@types/http-assert": "*",
"@types/http-errors": "*",
"@types/keygrip": "*",
"@types/koa-compose": "*",
"@types/node": "*"
}
},
"node_modules/@types/koa-compose": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz",
"integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==",
"dev": true,
"dependencies": {
"@types/koa": "*"
}
},
"node_modules/@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
@ -3708,6 +3780,17 @@
"@types/express": "*"
}
},
"node_modules/@types/passport-http-bearer": {
"version": "1.0.37",
"resolved": "https://registry.npmjs.org/@types/passport-http-bearer/-/passport-http-bearer-1.0.37.tgz",
"integrity": "sha512-/2Z28LfgY7kP/GO75os+feTP+//qHfpYn3V7sWAl0kwNwyDT1eGgjO30OU+Lown00ogSee+fea8a0+fr/UpTXw==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/koa": "*",
"@types/passport": "*"
}
},
"node_modules/@types/passport-jwt": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz",
@ -5348,18 +5431,9 @@
}
},
"node_modules/convert-source-map": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
"dev": true,
"dependencies": {
"safe-buffer": "~5.1.1"
}
},
"node_modules/convert-source-map/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
},
"node_modules/cookie": {
@ -7114,9 +7188,9 @@
"dev": true
},
"node_modules/globals": {
"version": "13.19.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
"integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
"integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
@ -7293,9 +7367,9 @@
}
},
"node_modules/help-me/node_modules/glob": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
"integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@ -7312,9 +7386,9 @@
}
},
"node_modules/help-me/node_modules/minimatch": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@ -8768,12 +8842,12 @@
}
},
"node_modules/jest-util": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz",
"integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.3.tgz",
"integrity": "sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==",
"dev": true,
"dependencies": {
"@jest/types": "^29.3.1",
"@jest/types": "^29.4.3",
"@types/node": "*",
"chalk": "^4.0.0",
"ci-info": "^3.2.0",
@ -8869,13 +8943,13 @@
}
},
"node_modules/jest-worker": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz",
"integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.3.tgz",
"integrity": "sha512-GLHN/GTAAMEy5BFdvpUfzr9Dr80zQqBrh0fz1mtRMe05hqP45+HfQltu7oTBfduD0UeZs09d+maFtFYAXFWvAA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"jest-util": "^29.3.1",
"jest-util": "^29.4.3",
"merge-stream": "^2.0.0",
"supports-color": "^8.0.0"
},
@ -10027,6 +10101,17 @@
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-http-bearer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz",
"integrity": "sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==",
"dependencies": {
"passport-strategy": "1.x.x"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/passport-jwt": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
@ -10316,9 +10401,9 @@
}
},
"node_modules/pino-abstract-transport/node_modules/readable-stream": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz",
"integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
"integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
@ -10390,9 +10475,9 @@
}
},
"node_modules/pino-pretty/node_modules/readable-stream": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz",
"integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
"integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
"dev": true,
"dependencies": {
"abort-controller": "^3.0.0",
@ -12904,17 +12989,17 @@
}
},
"node_modules/yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz",
"integrity": "sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ==",
"dependencies": {
"cliui": "^7.0.2",
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
@ -12928,6 +13013,19 @@
"node": ">=12"
}
},
"node_modules/yargs/node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
@ -14016,12 +14114,12 @@
}
},
"@jest/schemas": {
"version": "29.0.0",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz",
"integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz",
"integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==",
"dev": true,
"requires": {
"@sinclair/typebox": "^0.24.1"
"@sinclair/typebox": "^0.25.16"
}
},
"@jest/source-map": {
@ -14101,12 +14199,12 @@
}
},
"@jest/types": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz",
"integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.3.tgz",
"integrity": "sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==",
"dev": true,
"requires": {
"@jest/schemas": "^29.0.0",
"@jest/schemas": "^29.4.3",
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
@ -14178,13 +14276,13 @@
"devOptional": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
"version": "0.3.17",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
"dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
"@jridgewell/resolve-uri": "3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"@narando/nest-axios-interceptor": {
@ -14805,11 +14903,11 @@
},
"dependencies": {
"@opentelemetry/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.8.0.tgz",
"integrity": "sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.9.1.tgz",
"integrity": "sha512-6/qon6tw2I8ZaJnHAQUUn4BqhTbTNRS0WP8/bA0ynaX+Uzp/DDbd0NS0Cq6TMlh8+mrlsyqDE7mO50nmv2Yvlg==",
"requires": {
"@opentelemetry/semantic-conventions": "1.8.0"
"@opentelemetry/semantic-conventions": "1.9.1"
}
},
"@opentelemetry/instrumentation": {
@ -14823,9 +14921,9 @@
}
},
"@opentelemetry/semantic-conventions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.8.0.tgz",
"integrity": "sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew=="
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz",
"integrity": "sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg=="
},
"@types/express": {
"version": "4.17.13",
@ -14895,11 +14993,11 @@
},
"dependencies": {
"@opentelemetry/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.8.0.tgz",
"integrity": "sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.9.1.tgz",
"integrity": "sha512-6/qon6tw2I8ZaJnHAQUUn4BqhTbTNRS0WP8/bA0ynaX+Uzp/DDbd0NS0Cq6TMlh8+mrlsyqDE7mO50nmv2Yvlg==",
"requires": {
"@opentelemetry/semantic-conventions": "1.8.0"
"@opentelemetry/semantic-conventions": "1.9.1"
}
},
"@opentelemetry/instrumentation": {
@ -14913,9 +15011,9 @@
}
},
"@opentelemetry/semantic-conventions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.8.0.tgz",
"integrity": "sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew=="
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz",
"integrity": "sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg=="
}
}
},
@ -15328,9 +15426,9 @@
}
},
"@sinclair/typebox": {
"version": "0.24.43",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.43.tgz",
"integrity": "sha512-1orQTvtazZmsPeBroJjysvsOQCYV2yjWlebkSY38pl5vr2tdLjEJ+LoxITlGNZaH2RE19WlAwQMkH/7C14wLfw==",
"version": "0.25.23",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.23.tgz",
"integrity": "sha512-VEB8ygeP42CFLWyAJhN5OklpxUliqdNEUcXb4xZ/CINqtYGTjL5ukluKdKzQ0iWdUxyQ7B0539PAUhHKrCNWSQ==",
"dev": true
},
"@sinonjs/commons": {
@ -15380,6 +15478,15 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"devOptional": true
},
"@types/accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/babel__core": {
"version": "7.1.19",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz",
@ -15438,6 +15545,12 @@
"@types/node": "*"
}
},
"@types/content-disposition": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz",
"integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==",
"dev": true
},
"@types/cookie-parser": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz",
@ -15453,6 +15566,18 @@
"integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==",
"dev": true
},
"@types/cookies": {
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz",
"integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/express": "*",
"@types/keygrip": "*",
"@types/node": "*"
}
},
"@types/eslint": {
"version": "8.4.6",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
@ -15516,6 +15641,18 @@
"integrity": "sha512-oOMFT8vmCTFncsF1engrs04jatz8/Anwx3De9uxnOK4chgSEgWBvFtpSoJo8u3784JNO+ql5tzRR6phHoRnscQ==",
"dev": true
},
"@types/http-assert": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz",
"integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==",
"dev": true
},
"@types/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==",
"dev": true
},
"@types/istanbul-lib-coverage": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@ -15570,6 +15707,37 @@
"@types/node": "*"
}
},
"@types/keygrip": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
"integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==",
"dev": true
},
"@types/koa": {
"version": "2.13.5",
"resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.5.tgz",
"integrity": "sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA==",
"dev": true,
"requires": {
"@types/accepts": "*",
"@types/content-disposition": "*",
"@types/cookies": "*",
"@types/http-assert": "*",
"@types/http-errors": "*",
"@types/keygrip": "*",
"@types/koa-compose": "*",
"@types/node": "*"
}
},
"@types/koa-compose": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz",
"integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==",
"dev": true,
"requires": {
"@types/koa": "*"
}
},
"@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
@ -15600,6 +15768,17 @@
"@types/express": "*"
}
},
"@types/passport-http-bearer": {
"version": "1.0.37",
"resolved": "https://registry.npmjs.org/@types/passport-http-bearer/-/passport-http-bearer-1.0.37.tgz",
"integrity": "sha512-/2Z28LfgY7kP/GO75os+feTP+//qHfpYn3V7sWAl0kwNwyDT1eGgjO30OU+Lown00ogSee+fea8a0+fr/UpTXw==",
"dev": true,
"requires": {
"@types/express": "*",
"@types/koa": "*",
"@types/passport": "*"
}
},
"@types/passport-jwt": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz",
@ -16834,21 +17013,10 @@
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"convert-source-map": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
}
}
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
},
"cookie": {
"version": "0.4.1",
@ -18201,9 +18369,9 @@
"dev": true
},
"globals": {
"version": "13.19.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz",
"integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==",
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
"integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
"dev": true,
"requires": {
"type-fest": "^0.20.2"
@ -18327,9 +18495,9 @@
}
},
"glob": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
"integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
@ -18340,9 +18508,9 @@
}
},
"minimatch": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
@ -19404,12 +19572,12 @@
}
},
"jest-util": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz",
"integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.3.tgz",
"integrity": "sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==",
"dev": true,
"requires": {
"@jest/types": "^29.3.1",
"@jest/types": "^29.4.3",
"@types/node": "*",
"chalk": "^4.0.0",
"ci-info": "^3.2.0",
@ -19484,13 +19652,13 @@
}
},
"jest-worker": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz",
"integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==",
"version": "29.4.3",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.3.tgz",
"integrity": "sha512-GLHN/GTAAMEy5BFdvpUfzr9Dr80zQqBrh0fz1mtRMe05hqP45+HfQltu7oTBfduD0UeZs09d+maFtFYAXFWvAA==",
"dev": true,
"requires": {
"@types/node": "*",
"jest-util": "^29.3.1",
"jest-util": "^29.4.3",
"merge-stream": "^2.0.0",
"supports-color": "^8.0.0"
},
@ -20364,6 +20532,14 @@
"utils-merge": "^1.0.1"
}
},
"passport-http-bearer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz",
"integrity": "sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==",
"requires": {
"passport-strategy": "1.x.x"
}
},
"passport-jwt": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
@ -20574,9 +20750,9 @@
}
},
"readable-stream": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz",
"integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
"integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
"requires": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
@ -20630,9 +20806,9 @@
}
},
"readable-stream": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz",
"integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz",
"integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==",
"dev": true,
"requires": {
"abort-controller": "^3.0.0",
@ -22390,17 +22566,29 @@
"dev": true
},
"yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
"integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==",
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz",
"integrity": "sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ==",
"requires": {
"cliui": "^7.0.2",
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.0.0"
"yargs-parser": "^21.1.1"
},
"dependencies": {
"cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
}
}
}
},
"yargs-parser": {

View file

@ -67,6 +67,7 @@
"nestjs-pino": "3.1.2",
"nestjs-typeorm-paginate": "4.0.3",
"passport": "0.6.0",
"passport-http-bearer": "^1.0.1",
"passport-jwt": "4.0.1",
"passport-spotify": "2.0.0",
"pg": "8.8.0",
@ -87,6 +88,7 @@
"@types/jest": "29.2.5",
"@types/long": "4.0.2",
"@types/node": "18.11.19",
"@types/passport-http-bearer": "^1.0.37",
"@types/passport-jwt": "3.0.8",
"@types/supertest": "2.0.12",
"@typescript-eslint/eslint-plugin": "5.48.1",

View file

@ -0,0 +1,32 @@
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { User } from "../users/user.entity";
@Entity()
export class ApiToken {
@PrimaryGeneratedColumn("uuid")
id: string;
@ManyToOne(() => User, { eager: true })
user: User;
@Column()
description: string;
@Column({ unique: true })
token: string;
@CreateDateColumn()
createdAt: Date;
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
lastUsedAt: Date;
@Column({ type: "timestamp", nullable: true })
revokedAt: Date | null;
}

View file

@ -0,0 +1,24 @@
/* eslint-disable max-classes-per-file */
import { Repository, SelectQueryBuilder } from "typeorm";
import { EntityRepository } from "../database/entity-repository";
import { User } from "../users/user.entity";
import { ApiToken } from "./api-token.entity";
export class ApiTokenScopes extends SelectQueryBuilder<ApiToken> {
/**
* `byUser` scopes the query to ApiTokens created by the user.
* @param currentUser
*/
byUser(currentUser: User): this {
return this.andWhere(`token."userId" = :userID`, {
userID: currentUser.id,
});
}
}
@EntityRepository(ApiToken)
export class ApiTokenRepository extends Repository<ApiToken> {
get scoped(): ApiTokenScopes {
return new ApiTokenScopes(this.createQueryBuilder("token"));
}
}

View file

@ -1,19 +1,25 @@
import {
Body,
Controller,
Delete,
Get,
Post,
Res,
UseFilters,
UseGuards,
} from "@nestjs/common";
import { ApiTags } from "@nestjs/swagger";
import { ApiBody, ApiTags } from "@nestjs/swagger";
import type { Response } from "express";
import { User } from "../users/user.entity";
import { ApiToken } from "./api-token.entity";
import { AuthSession } from "./auth-session.entity";
import { AuthService } from "./auth.service";
import { COOKIE_REFRESH_TOKEN } from "./constants";
import { AuthAccessToken } from "./decorators/auth-access-token.decorator";
import { ReqUser } from "./decorators/req-user.decorator";
import { CreateApiTokenRequestDto } from "./dto/create-api-token-request.dto";
import { RefreshAccessTokenResponseDto } from "./dto/refresh-access-token-response.dto";
import { RevokeApiTokenRequestDto } from "./dto/revoke-api-token-request.dto";
import {
RefreshTokenAuthGuard,
SpotifyAuthGuard,
@ -55,4 +61,30 @@ export class AuthController {
return { accessToken };
}
@Post("api-tokens")
@ApiBody({ type: CreateApiTokenRequestDto })
@AuthAccessToken()
async createApiToken(
@ReqUser() user: User,
@Body("description") description: string
): Promise<ApiToken> {
return this.authService.createApiToken(user, description);
}
@Get("api-tokens")
@AuthAccessToken()
async listApiTokens(@ReqUser() user: User): Promise<ApiToken[]> {
return this.authService.listApiTokens(user);
}
// This endpoint does not validate that the token belongs to the logged in user.
// Once the token is known, it does not matter which account makes the actual
// request to revoke it.
@Delete("api-tokens")
@ApiBody({ type: RevokeApiTokenRequestDto })
@AuthAccessToken()
async revokeApiToken(@Body("token") token: string): Promise<void> {
return this.authService.revokeApiToken(token);
}
}

View file

@ -5,16 +5,18 @@ import { PassportModule } from "@nestjs/passport";
import { CookieParserMiddleware } from "../cookie-parser";
import { TypeOrmRepositoryModule } from "../database/entity-repository/typeorm-repository.module";
import { UsersModule } from "../users/users.module";
import { ApiTokenRepository } from "./api-token.repository";
import { AuthSessionRepository } from "./auth-session.repository";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
import { AccessTokenStrategy } from "./strategies/access-token.strategy";
import { ApiTokenStrategy } from "./strategies/api-token.strategy";
import { RefreshTokenStrategy } from "./strategies/refresh-token.strategy";
import { SpotifyStrategy } from "./strategies/spotify.strategy";
@Module({
imports: [
TypeOrmRepositoryModule.for([AuthSessionRepository]),
TypeOrmRepositoryModule.for([AuthSessionRepository, ApiTokenRepository]),
PassportModule.register({ defaultStrategy: "jwt" }),
JwtModule.registerAsync({
useFactory: (config: ConfigService) => ({
@ -33,6 +35,7 @@ import { SpotifyStrategy } from "./strategies/spotify.strategy";
SpotifyStrategy,
AccessTokenStrategy,
RefreshTokenStrategy,
ApiTokenStrategy,
],
exports: [PassportModule],
controllers: [AuthController],

View file

@ -4,6 +4,7 @@ import { JwtService } from "@nestjs/jwt";
import { Test, TestingModule } from "@nestjs/testing";
import { User } from "../users/user.entity";
import { UsersService } from "../users/users.service";
import { ApiTokenRepository } from "./api-token.repository";
import { AuthSession } from "./auth-session.entity";
import { AuthSessionRepository } from "./auth-session.repository";
import { AuthService } from "./auth.service";
@ -15,6 +16,7 @@ describe("AuthService", () => {
let usersService: UsersService;
let jwtService: JwtService;
let authSessionRepository: AuthSessionRepository;
let apiTokenRepository: ApiTokenRepository;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
@ -27,6 +29,7 @@ describe("AuthService", () => {
{ provide: UsersService, useFactory: () => ({}) },
{ provide: JwtService, useFactory: () => ({}) },
{ provide: AuthSessionRepository, useFactory: () => ({}) },
{ provide: ApiTokenRepository, useFactory: () => ({}) },
],
}).compile();
@ -37,6 +40,7 @@ describe("AuthService", () => {
authSessionRepository = module.get<AuthSessionRepository>(
AuthSessionRepository
);
apiTokenRepository = module.get<ApiTokenRepository>(ApiTokenRepository);
});
it("should be defined", () => {
@ -45,6 +49,7 @@ describe("AuthService", () => {
expect(usersService).toBeDefined();
expect(jwtService).toBeDefined();
expect(authSessionRepository).toBeDefined();
expect(apiTokenRepository).toBeDefined();
});
describe("spotifyLogin", () => {

View file

@ -3,9 +3,12 @@ import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt";
import { User } from "../users/user.entity";
import { UsersService } from "../users/users.service";
import { ApiToken } from "./api-token.entity";
import { ApiTokenRepository } from "./api-token.repository";
import { AuthSession } from "./auth-session.entity";
import { AuthSessionRepository } from "./auth-session.repository";
import { LoginDto } from "./dto/login.dto";
import { randomBytes } from "crypto";
@Injectable()
export class AuthService {
@ -16,7 +19,8 @@ export class AuthService {
private readonly config: ConfigService,
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
private readonly authSessionRepository: AuthSessionRepository
private readonly authSessionRepository: AuthSessionRepository,
private readonly apiTokenRepository: ApiTokenRepository
) {
this.userFilter = this.config.get<string>("SPOTIFY_USER_FILTER");
this.sessionExpirationTime = this.config.get<string>(
@ -66,7 +70,7 @@ export class AuthService {
*/
private async createRefreshToken(
session: AuthSession
): Promise<{ refreshToken }> {
): Promise<{ refreshToken: string }> {
const payload = {
sub: session.user.id,
name: session.user.displayName,
@ -81,7 +85,9 @@ export class AuthService {
return { refreshToken: token };
}
async createAccessToken(session: AuthSession): Promise<{ accessToken }> {
async createAccessToken(
session: AuthSession
): Promise<{ accessToken: string }> {
if (session.revokedAt) {
throw new ForbiddenException("SessionIsRevoked");
}
@ -101,6 +107,41 @@ export class AuthService {
return this.authSessionRepository.findOneBy({ id });
}
async createApiToken(user: User, description: string): Promise<ApiToken> {
console.log("createApiToken");
const apiToken = this.apiTokenRepository.create();
apiToken.user = user;
apiToken.description = description;
// TODO demagic 20
const tokenBuffer = await new Promise<Buffer>((resolve, reject) =>
randomBytes(20, (err, buf) => (err ? reject(err) : resolve(buf)))
);
apiToken.token = `lis${tokenBuffer.toString("hex")}`;
await this.apiTokenRepository.save(apiToken);
return apiToken;
}
async listApiTokens(user: User): Promise<ApiToken[]> {
return this.apiTokenRepository.scoped.byUser(user).getMany();
}
async revokeApiToken(token: string): Promise<void> {
const apiToken = await this.findApiToken(token);
apiToken.revokedAt = new Date();
await this.apiTokenRepository.save(apiToken);
return;
}
async findApiToken(token: string): Promise<ApiToken> {
return this.apiTokenRepository.findOneBy({ token });
}
async findUser(id: string): Promise<User> {
return this.usersService.findById(id);
}

View file

@ -1,11 +1,11 @@
import { applyDecorators, UseGuards } from "@nestjs/common";
import { ApiBearerAuth, ApiUnauthorizedResponse } from "@nestjs/swagger";
import { AccessTokenAuthGuard } from "../guards/auth-strategies.guard";
import { ApiAuthGuard } from "../guards/auth-strategies.guard";
export function AuthAccessToken() {
return applyDecorators(
UseGuards(AccessTokenAuthGuard),
UseGuards(ApiAuthGuard),
ApiBearerAuth(),
ApiUnauthorizedResponse({ description: 'Unauthorized"' })
ApiUnauthorizedResponse({ description: "Unauthorized" })
);
}

View file

@ -0,0 +1,9 @@
import { ApiProperty } from "@nestjs/swagger";
export class CreateApiTokenRequestDto {
@ApiProperty({
description: "Opaque text field to identify the API token",
example: "My super duper token",
})
description: string;
}

View file

@ -0,0 +1,9 @@
import { ApiProperty } from "@nestjs/swagger";
export class RevokeApiTokenRequestDto {
@ApiProperty({
description: "The API Token that should be revoked",
example: "lisasdasdjaksr2381asd",
})
token: string;
}

View file

@ -2,7 +2,10 @@ import { AuthGuard } from "@nestjs/passport";
import { AuthStrategy } from "../strategies/strategies.enum";
// Internal
export const AccessTokenAuthGuard = AuthGuard(AuthStrategy.AccessToken);
export const ApiAuthGuard = AuthGuard([
AuthStrategy.AccessToken,
AuthStrategy.ApiToken,
]);
export const RefreshTokenAuthGuard = AuthGuard(AuthStrategy.RefreshToken);
// Auth Provider

View file

@ -0,0 +1,34 @@
import {
Injectable,
UnauthorizedException,
ForbiddenException,
} from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-http-bearer";
import { User } from "../../users/user.entity";
import { AuthService } from "../auth.service";
import { AuthStrategy } from "./strategies.enum";
@Injectable()
export class ApiTokenStrategy extends PassportStrategy(
Strategy,
AuthStrategy.ApiToken
) {
constructor(private readonly authService: AuthService) {
super();
}
async validate(token: string): Promise<User> {
const apiToken = await this.authService.findApiToken(token);
if (!apiToken) {
throw new UnauthorizedException("TokenNotFound");
}
if (apiToken.revokedAt) {
throw new ForbiddenException("TokenIsRevoked");
}
return apiToken.user;
}
}

View file

@ -2,6 +2,7 @@ export enum AuthStrategy {
// Internal
AccessToken = "access_token",
RefreshToken = "refresh_token",
ApiToken = "api_token",
// Auth Provider
Spotify = "spotify",

View file

@ -0,0 +1,77 @@
import {
MigrationInterface,
QueryRunner,
Table,
TableIndex,
TableForeignKey,
} from "typeorm";
import { TableColumnOptions } from "typeorm/schema-builder/options/TableColumnOptions";
const primaryUUIDColumn: TableColumnOptions = {
name: "id",
type: "uuid",
isPrimary: true,
isGenerated: true,
generationStrategy: "uuid",
};
export class CreateApiTokensTable0000000000007 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: "api_token",
columns: [
primaryUUIDColumn,
{
name: "userId",
type: "uuid",
},
{
name: "description",
type: "varchar",
},
{
name: "token",
type: "varchar",
isUnique: true,
},
{
name: "createdAt",
type: "timestamp",
default: "CURRENT_TIMESTAMP",
},
{
name: "lastUsedAt",
type: "timestamp",
default: "CURRENT_TIMESTAMP",
},
{
name: "revokedAt",
type: "timestamp",
default: null,
isNullable: true,
},
],
indices: [
new TableIndex({
name: "IDX_API_TOKEN_USER_ID",
columnNames: ["userId"],
}),
],
foreignKeys: [
new TableForeignKey({
name: "FK_API_TOKEN_USER_ID",
columnNames: ["userId"],
referencedColumnNames: ["id"],
referencedTableName: "user",
}),
],
}),
true
);
}
async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable("api_token");
}
}