diff --git a/package-lock.json b/package-lock.json index d9761e2..1ceb16b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1589,6 +1589,25 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, + "node_modules/@auth0/auth0-angular": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@auth0/auth0-angular/-/auth0-angular-2.2.1.tgz", + "integrity": "sha512-ie9CMNOdQ3mkhhe09SEporCayDuuPYHNmctyHeV6caVgZVSSu9FlFt2ec6CkX27ufDq7yCdyJGnDixWxNtUG+Q==", + "dependencies": { + "@auth0/auth0-spa-js": "^2.0.1", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=13", + "@angular/core": ">=13", + "@angular/router": ">=13" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.2.tgz", + "integrity": "sha512-xdA65Z/U7++Y7L9Uwh8Q8OVOs6qgFz+fb7GAzHFjpr1icO37B//xdzLXm7ZRgA19RWrsNe1nme3h896igJSvvw==" + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -5881,6 +5900,15 @@ "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/@nestjs/passport": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.2.tgz", + "integrity": "sha512-od31vfB2z3y05IDB5dWSbCGE2+pAf2k2WCBinNuTTOxN0O0+wtO1L3kawj/aCW3YR9uxsTOVbTDwtwgpNNsnjQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.2.9", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.2.9.tgz", @@ -7250,7 +7278,6 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -7269,7 +7296,6 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -7352,7 +7378,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -7364,7 +7389,6 @@ "version": "4.17.41", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -7384,8 +7408,7 @@ "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/http-proxy": { "version": "1.17.14", @@ -7459,8 +7482,7 @@ "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/minimatch": { "version": "3.0.5", @@ -7511,17 +7533,45 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/@types/passport": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.13.tgz", + "integrity": "sha512-fjHaC6Bv8EpMMqzTnHP32SXlZGaNfBPC/Po5dmRGYi2Ky7ljXPbGnOy+SxZqa6iZvFgVhoJ1915Re3m93zmcfA==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.10", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", - "dev": true + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/retry": { "version": "0.12.0", @@ -7539,7 +7589,6 @@ "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -7570,7 +7619,6 @@ "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -14775,6 +14823,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", + "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-beautify": { "version": "1.14.11", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", @@ -15018,6 +15074,22 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", @@ -15669,8 +15741,7 @@ "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", - "dev": true + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, "node_modules/lines-and-columns": { "version": "2.0.4", @@ -15830,6 +15901,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -15938,6 +16014,29 @@ "node": ">=10" } }, + "node_modules/lru-memoizer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", + "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, "node_modules/lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", @@ -18235,6 +18334,41 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "peer": true, + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -18313,6 +18447,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", @@ -18719,6 +18858,11 @@ "dev": true, "optional": true }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -23764,9 +23908,13 @@ "@nestjs/core": "^10.0.0", "@nestjs/event-emitter": "^2.0.3", "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", "@nestjs/sequelize": "^10.0.0", "bridge-shared": "^1.0.0", + "jwks-rsa": "^3.1.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "sequelize": "^6.35.1", @@ -23783,6 +23931,7 @@ "@types/jest": "^29.5.2", "@types/multer": "^1.4.11", "@types/node": "^20.3.1", + "@types/passport-jwt": "^3.0.13", "@types/sequelize": "^4.28.18", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", @@ -23801,6 +23950,23 @@ "typescript": "^5.1.3" } }, + "packages/bridge-server/node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, "packages/bridge-shared": { "version": "1.0.0", "license": "ISC", @@ -23836,6 +24002,7 @@ "@angular/platform-browser": "^17.0.0", "@angular/platform-browser-dynamic": "^17.0.0", "@angular/router": "^17.0.0", + "@auth0/auth0-angular": "^2.2.1", "bridge-shared": "^1.0.0", "eventemitter3": "^5.0.1", "jwt-decode": "^4.0.0", diff --git a/packages/bridge-server/.env b/packages/bridge-server/.env new file mode 100644 index 0000000..1949dce --- /dev/null +++ b/packages/bridge-server/.env @@ -0,0 +1,2 @@ +AUTH0_ISSUER_URL=https://dev-0pjms7kv5foqe0ex.uk.auth0.com/ +AUTH0_AUDIENCE=https://ponyta.pkmn.cloud \ No newline at end of file diff --git a/packages/bridge-server/.vscode/launch.json b/packages/bridge-server/.vscode/launch.json new file mode 100644 index 0000000..814dd95 --- /dev/null +++ b/packages/bridge-server/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\src\\authz\\authz.guard.ts", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/packages/bridge-server/package.json b/packages/bridge-server/package.json index 2c92998..33fdb0e 100644 --- a/packages/bridge-server/package.json +++ b/packages/bridge-server/package.json @@ -24,9 +24,13 @@ "@nestjs/core": "^10.0.0", "@nestjs/event-emitter": "^2.0.3", "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", "@nestjs/sequelize": "^10.0.0", "bridge-shared": "^1.0.0", + "jwks-rsa": "^3.1.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "sequelize": "^6.35.1", @@ -43,6 +47,7 @@ "@types/jest": "^29.5.2", "@types/multer": "^1.4.11", "@types/node": "^20.3.1", + "@types/passport-jwt": "^3.0.13", "@types/sequelize": "^4.28.18", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", diff --git a/packages/bridge-server/src/app.module.ts b/packages/bridge-server/src/app.module.ts index 0b625f4..bdaa869 100644 --- a/packages/bridge-server/src/app.module.ts +++ b/packages/bridge-server/src/app.module.ts @@ -25,6 +25,7 @@ import { SeasonStandingsModule } from './season-standings/season-standings.modul import { UploadModule } from './upload/upload.module'; import { SseService } from './sse/sse.service'; import { SseModule } from './sse/sse.module'; +import { AuthzModule } from './authz/authz.module'; @Module({ imports: [ @@ -62,6 +63,7 @@ import { SseModule } from './sse/sse.module'; SeasonStandingsModule, UploadModule, SseModule, + AuthzModule, ], controllers: [AppController, SeasonsController, UploadController], providers: [AppService, UsersService, RacersService, RacesService, SeasonStandingsService, RaceResultsService, SseService], diff --git a/packages/bridge-server/src/authz/authz.controller.ts b/packages/bridge-server/src/authz/authz.controller.ts new file mode 100644 index 0000000..434c988 --- /dev/null +++ b/packages/bridge-server/src/authz/authz.controller.ts @@ -0,0 +1,22 @@ +import { + Body, + Controller, + Get, + HttpCode, + HttpStatus, + Post, + Request, + UseGuards + } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + + @Controller('authz') + export class AuthzController { + constructor() {} + + @UseGuards(AuthGuard('jwt')) + @Get('test') + getProfile(@Request() req) { + return "Hello"; + } + } \ No newline at end of file diff --git a/packages/bridge-server/src/authz/authz.guard.ts b/packages/bridge-server/src/authz/authz.guard.ts new file mode 100644 index 0000000..053c56e --- /dev/null +++ b/packages/bridge-server/src/authz/authz.guard.ts @@ -0,0 +1,26 @@ +import { + ExecutionContext, + Injectable, + UnauthorizedException, + } from '@nestjs/common'; + +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') { + canActivate(context: ExecutionContext) { + console.log("runnign") + // Add your custom authentication logic here + // for example, call super.logIn(request) to establish a session. + return super.canActivate(context); + } + + handleRequest(err, user, info) { + console.log("runnign!!") + // You can throw an exception based on either "info" or "err" arguments + if (err || !user) { + throw err || new UnauthorizedException(); + } + return user; + } + } \ No newline at end of file diff --git a/packages/bridge-server/src/authz/authz.module.ts b/packages/bridge-server/src/authz/authz.module.ts new file mode 100644 index 0000000..e135cd6 --- /dev/null +++ b/packages/bridge-server/src/authz/authz.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; +import { JwtStrategy } from './jwt.strategy'; +import { AuthzController } from './authz.controller'; + +@Module({ + imports: [PassportModule.register({ defaultStrategy: 'jwt' })], + providers: [JwtStrategy], + controllers: [AuthzController], + exports: [PassportModule], +}) +export class AuthzModule {} diff --git a/packages/bridge-server/src/authz/jwt.strategy.ts b/packages/bridge-server/src/authz/jwt.strategy.ts new file mode 100644 index 0000000..4a3d970 --- /dev/null +++ b/packages/bridge-server/src/authz/jwt.strategy.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { passportJwtSecret } from 'jwks-rsa'; +import * as dotenv from 'dotenv'; + +dotenv.config(); + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { + constructor() { + super({ + secretOrKeyProvider: passportJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `${process.env.AUTH0_ISSUER_URL}.well-known/jwks.json`, + }), + + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + audience: process.env.AUTH0_AUDIENCE, + issuer: `${process.env.AUTH0_ISSUER_URL}`, + algorithms: ['RS256'], + }); + } + + validate(payload: unknown): unknown { + console.log(payload) + return payload; + } + + +} \ No newline at end of file diff --git a/packages/bridge-ui/package.json b/packages/bridge-ui/package.json index d40522e..76260f8 100644 --- a/packages/bridge-ui/package.json +++ b/packages/bridge-ui/package.json @@ -20,6 +20,7 @@ "@angular/platform-browser": "^17.0.0", "@angular/platform-browser-dynamic": "^17.0.0", "@angular/router": "^17.0.0", + "@auth0/auth0-angular": "^2.2.1", "bridge-shared": "^1.0.0", "eventemitter3": "^5.0.1", "jwt-decode": "^4.0.0", diff --git a/packages/bridge-ui/src/app/app.config.ts b/packages/bridge-ui/src/app/app.config.ts index 35c1101..09fc0cd 100644 --- a/packages/bridge-ui/src/app/app.config.ts +++ b/packages/bridge-ui/src/app/app.config.ts @@ -7,6 +7,7 @@ import { provideAnimations } from '@angular/platform-browser/animations'; import { routes } from './app.routes'; import { AuthInterceptor } from './interceptors/auth.interceptor'; import { SnackbarInterceptor } from './interceptors/snackbar.interceptor'; +import { AuthHttpInterceptor, AuthModule } from '@auth0/auth0-angular'; export const appConfig: ApplicationConfig = { providers: [ @@ -16,7 +17,31 @@ export const appConfig: ApplicationConfig = { withInterceptorsFromDi() ), importProvidersFrom(MatNativeDateModule), - {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}, + {provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true}, {provide: HTTP_INTERCEPTORS, useClass: SnackbarInterceptor, multi: true}, + importProvidersFrom(AuthModule.forRoot({ + domain: 'dev-0pjms7kv5foqe0ex.uk.auth0.com', + clientId: 'GZFNgt0sFgBZFbj5uBK5s1cRZELYUstm', + + authorizationParams: { + redirect_uri: window.location.origin, + audience: 'https://ponyta.pkmn.cloud' + }, + httpInterceptor: { + allowedList: [ + { + // Match any request that starts 'https://{yourDomain}/api/v2/' (note the asterisk) + //uri: 'https://ponyta.pkmn.cloud/*', + uri: 'http://localhost:3000/*', + tokenOptions: { + authorizationParams: { + // The attached token should target this audience + audience: 'https://ponyta.pkmn.cloud' + } + } + } + ] + } + })) ] }; diff --git a/packages/bridge-ui/src/app/components/login-dialog/login-dialog.component.ts b/packages/bridge-ui/src/app/components/login-dialog/login-dialog.component.ts index fe7a2cb..71c182e 100644 --- a/packages/bridge-ui/src/app/components/login-dialog/login-dialog.component.ts +++ b/packages/bridge-ui/src/app/components/login-dialog/login-dialog.component.ts @@ -49,6 +49,7 @@ export class LoginDialogComponent { let username = this.username.value != undefined ? this.username.value : ""; let password = this.password.value != undefined ? this.password.value : ""; + /* this.authService.login(username, password).subscribe(data => { console.log(data) if (data.access_token) { @@ -56,5 +57,6 @@ export class LoginDialogComponent { window.location.reload(); } }) + */ } } diff --git a/packages/bridge-ui/src/app/components/top-bar/top-bar.component.html b/packages/bridge-ui/src/app/components/top-bar/top-bar.component.html index 8322a66..2005461 100644 --- a/packages/bridge-ui/src/app/components/top-bar/top-bar.component.html +++ b/packages/bridge-ui/src/app/components/top-bar/top-bar.component.html @@ -12,13 +12,16 @@ [options] = "options" (themeChange)="themeChangeHandler($event)"> - @if(isAuthed() == false) { + + @if(isAuthenticated == false) { } - @if(isAuthed()) { - } diff --git a/packages/bridge-ui/src/app/components/top-bar/top-bar.component.ts b/packages/bridge-ui/src/app/components/top-bar/top-bar.component.ts index 450f1b0..8392843 100644 --- a/packages/bridge-ui/src/app/components/top-bar/top-bar.component.ts +++ b/packages/bridge-ui/src/app/components/top-bar/top-bar.component.ts @@ -16,6 +16,8 @@ import { ThemeOption } from '../../models/theme-option.model'; import { LoginDialogComponent } from '../login-dialog/login-dialog.component'; import { AuthService } from '../../services/auth.service'; +//import { AuthService } from '@auth0/auth0-angular'; + @Component({ selector: 'app-top-bar', standalone: true, @@ -34,11 +36,12 @@ import { AuthService } from '../../services/auth.service'; export class TopBarComponent { constructor( private readonly themeService: ThemeService, - private authService: AuthService, + public authService: AuthService, public dialog: MatDialog ) {} options: Array = []; + isAuthenticated: boolean = false; async ngOnInit() { this.themeService.getThemeOptions().subscribe(data => { @@ -51,7 +54,11 @@ export class TopBarComponent { } onLoginClick() { - this.dialog.open(LoginDialogComponent); + this.authService.login(); + } + + onLogoutClick() { + this.authService.logout(); } isAuthed() { diff --git a/packages/bridge-ui/src/app/services/auth.service.ts b/packages/bridge-ui/src/app/services/auth.service.ts index 1bcf69e..7bf4f65 100644 --- a/packages/bridge-ui/src/app/services/auth.service.ts +++ b/packages/bridge-ui/src/app/services/auth.service.ts @@ -4,33 +4,57 @@ import { Observable } from 'rxjs'; import { jwtDecode } from "jwt-decode"; import { ServerEndpointService } from './server-endpoint.service'; +import { AuthService as Auth0Service } from '@auth0/auth0-angular'; + @Injectable({ providedIn: 'root' }) export class AuthService { + _isAuthenticated:boolean = false; + constructor( private httpClient: HttpClient, private serverEndpointService: ServerEndpointService, - ) { } + private auth0: Auth0Service + ) + { + localStorage.removeItem('token'); + + this.auth0.isAuthenticated$.subscribe(authed => { + this._isAuthenticated = authed; + }); - login(username: string, password: string): Observable { - const headers = new HttpHeaders(); - headers.append('Content-Type', 'application/json'); - return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"auth/login", { username, password }, { headers }) + this.auth0.user$.subscribe(user => { + console.log(user); + }) + + this.auth0.idTokenClaims$.subscribe(data => { + console.log(data) + if (data && data.__raw) { + localStorage.setItem('token', data.__raw); + } + }) + } + + login() { + this.auth0.loginWithRedirect(); } testProfile(): Observable { - console.log("SendTestProfile") - return this.httpClient.get(this.serverEndpointService.getCurrentEndpoint()+"auth/profile") + return this.httpClient.get(this.serverEndpointService.getCurrentEndpoint()+"authz/test") } logout(): void { - // Clear the token from local storage + this.auth0.logout(); localStorage.removeItem('token'); } isAuthenticated(): boolean { + if(!this._isAuthenticated) { + return false; + } + const token = localStorage.getItem('token'); if (!token) {