From 4a2c28834c8be5657ae5f978d6db155efea6d9de Mon Sep 17 00:00:00 2001 From: harshithnrao Date: Mon, 17 Mar 2025 10:57:01 +0530 Subject: [PATCH] auth+jwt and code reviewed --- create table queries.sql | 15 + package-lock.json | 613 ++++++++++++++++++++++++++- package.json | 7 + src/app-config/app-config.service.ts | 4 + src/app-config/config.json | 11 + src/app-config/config.local.json | 11 + src/app.module.ts | 6 +- src/app.service.ts | 4 + src/auth/auth.controller.ts | 103 +++++ src/auth/auth.module.ts | 33 ++ src/auth/auth.service.ts | 99 +++++ src/common/Utility.ts | 3 +- src/common/common.service.ts | 1 + src/jwt/jwt-auth.guard.ts | 5 + src/jwt/jwt-payload.dto.ts | 10 + src/jwt/jwt.strategy.ts | 20 + src/jwt/refresh-token.entity.ts | 14 + src/main.ts | 1 + src/user/user.module.ts | 1 + src/user/user.service.ts | 6 +- 20 files changed, 952 insertions(+), 15 deletions(-) create mode 100644 src/auth/auth.controller.ts create mode 100644 src/auth/auth.module.ts create mode 100644 src/auth/auth.service.ts create mode 100644 src/jwt/jwt-auth.guard.ts create mode 100644 src/jwt/jwt-payload.dto.ts create mode 100644 src/jwt/jwt.strategy.ts create mode 100644 src/jwt/refresh-token.entity.ts diff --git a/create table queries.sql b/create table queries.sql index f3a3bed..df2170e 100644 --- a/create table queries.sql +++ b/create table queries.sql @@ -96,3 +96,18 @@ CREATE TABLE "subscription" ( "deletedAt" DATE, "version" NUMERIC, ); + +CREATE TABLE "refresh_tokens" ( + "id" BIGSERIAL PRIMARY KEY, + "email" UNIQUE TEXT, + "token" TEXT, + "status" TEXT, + "validFrom" DATE, + "validTill" DATE, + "createdAt" DATE, + "updatedAt" DATE, + "createBy" TEXT, + "modifiedBy" TEXT, + "deletedAt" DATE, + "version" NUMERIC, +); diff --git a/package-lock.json b/package-lock.json index 774c3c8..1e9cc40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,19 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^11.0.0", + "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^8.1.1", "@nestjs/typeorm": "^10.0.1", + "bcrypt": "^5.1.1", "dotenv": "^16.3.1", "handlebars": "^4.7.8", "moment": "^2.30.1", "nodemailer": "^6.9.9", "otp-generator": "^4.0.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "pg": "^8.11.3", "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", @@ -31,11 +36,13 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", + "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/multer": "^1.4.11", "@types/node": "^20.10.6", "@types/nodemailer": "^6.4.14", + "@types/passport-jwt": "^4.0.1", "@types/supertest": "^2.0.12", "@types/validator": "^13.11.7", "@typescript-eslint/eslint-plugin": "^6.0.0", @@ -392,14 +399,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/types": "^7.26.10" }, "engines": { "node": ">=6.9.0" @@ -695,9 +702,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1633,6 +1640,62 @@ "node": ">=8" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", @@ -1864,6 +1927,19 @@ } } }, + "node_modules/@nestjs/jwt": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.0.tgz", + "integrity": "sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "9.0.7", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" + } + }, "node_modules/@nestjs/mapped-types": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz", @@ -1884,6 +1960,16 @@ } } }, + "node_modules/@nestjs/passport": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz", + "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "passport": "^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "10.4.15", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.15.tgz", @@ -2198,6 +2284,16 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -2352,6 +2448,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -2401,6 +2506,38 @@ "@types/node": "*" } }, + "node_modules/@types/passport": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@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, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -2917,6 +3054,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2966,6 +3109,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -3112,6 +3267,40 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -3428,6 +3617,20 @@ ], "license": "MIT" }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3610,6 +3813,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3786,6 +3995,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -4024,6 +4242,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4101,6 +4328,12 @@ "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", "license": "MIT" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -4336,6 +4569,12 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4355,6 +4594,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4469,6 +4717,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5453,6 +5710,36 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/fs-monkey": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", @@ -5490,6 +5777,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5771,6 +6085,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -5825,6 +6145,19 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -7102,6 +7435,61 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7185,6 +7573,42 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7199,6 +7623,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7455,6 +7885,37 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -7559,6 +8020,12 @@ "dev": true, "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -7701,6 +8168,21 @@ "node": ">=4" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7724,6 +8206,19 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7953,6 +8448,42 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "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==", + "license": "MIT", + "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", @@ -8026,6 +8557,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": { "version": "8.13.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.3.tgz", @@ -8663,7 +9199,6 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -8679,7 +9214,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -8691,7 +9225,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -8712,7 +9245,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -9079,6 +9611,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -9601,6 +10139,50 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/terser": { "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", @@ -10649,6 +11231,15 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wkx": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", diff --git a/package.json b/package.json index 82f8df3..3e724d1 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,19 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^11.0.0", + "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^8.1.1", "@nestjs/typeorm": "^10.0.1", + "bcrypt": "^5.1.1", "dotenv": "^16.3.1", "handlebars": "^4.7.8", "moment": "^2.30.1", "nodemailer": "^6.9.9", "otp-generator": "^4.0.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", "pg": "^8.11.3", "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", @@ -45,11 +50,13 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", + "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/multer": "^1.4.11", "@types/node": "^20.10.6", "@types/nodemailer": "^6.4.14", + "@types/passport-jwt": "^4.0.1", "@types/supertest": "^2.0.12", "@types/validator": "^13.11.7", "@typescript-eslint/eslint-plugin": "^6.0.0", diff --git a/src/app-config/app-config.service.ts b/src/app-config/app-config.service.ts index 8344725..c937b07 100644 --- a/src/app-config/app-config.service.ts +++ b/src/app-config/app-config.service.ts @@ -45,4 +45,8 @@ export class AppConfigService { getSwaggerConfig() { return configMaster[this.defaultEnv].swaggerConfig; } + + getJwtConfig() { + return configMaster[this.defaultEnv].jwtConfig; + } } diff --git a/src/app-config/config.json b/src/app-config/config.json index 4e4acc4..bed87c5 100644 --- a/src/app-config/config.json +++ b/src/app-config/config.json @@ -37,6 +37,17 @@ "version": "1.0.0", "tag": "Remedify Users API" } + }, + "jwtConfig": { + "accessToken":{ + + "secretOrKey":"8c0f4160ecc3764c2c749f64434ac77863e372319f2b236cb9a6541b30b830f1", + "expiresIn":"15m" + }, + "refreshToken":{ + "secretOrKey":"40a75dccc55f08f7558f99d97079aa2d2698a61ece0434d435f036f1c8fac21c", + "expiresIn":"7d" + } } } diff --git a/src/app-config/config.local.json b/src/app-config/config.local.json index 4e4acc4..bed87c5 100644 --- a/src/app-config/config.local.json +++ b/src/app-config/config.local.json @@ -37,6 +37,17 @@ "version": "1.0.0", "tag": "Remedify Users API" } + }, + "jwtConfig": { + "accessToken":{ + + "secretOrKey":"8c0f4160ecc3764c2c749f64434ac77863e372319f2b236cb9a6541b30b830f1", + "expiresIn":"15m" + }, + "refreshToken":{ + "secretOrKey":"40a75dccc55f08f7558f99d97079aa2d2698a61ece0434d435f036f1c8fac21c", + "expiresIn":"7d" + } } } diff --git a/src/app.module.ts b/src/app.module.ts index f9b58a3..e89d599 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { MasterConfigModule } from './master-config/master-config.module'; import { ConfigModule } from './config/config.module'; import { SubscriptionsModule } from './subscription/subscription.module'; import { InstituteModule } from './institute/institute.module'; +import { AuthModule } from './auth/auth.module'; @Module({ imports: [ UserModule, @@ -21,9 +22,10 @@ import { InstituteModule } from './institute/institute.module'; ItemsModule, SubscriptionsModule, // MasterConfigModule, - ConfigModule + ConfigModule, + AuthModule ], controllers: [AppController, AppConfigController], - providers: [CommonService, AppConfigService, AppService], + providers: [CommonService, AppConfigService, AppService,], }) export class AppModule {} diff --git a/src/app.service.ts b/src/app.service.ts index 37cf196..65349b6 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -45,6 +45,10 @@ export class AppService { const swaggerConfig = this.configService.getSwaggerConfig(); this.commonService.swaggerConfig = swaggerConfig; Utility.swaggerConfig = swaggerConfig; + + const jwtConfig = this.configService.getJwtConfig(); + this.commonService.jwtConfig = jwtConfig; + Utility.jwtConfig = jwtConfig; } getModels() { diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts new file mode 100644 index 0000000..e4bed98 --- /dev/null +++ b/src/auth/auth.controller.ts @@ -0,0 +1,103 @@ +import { Body, Controller, Delete, Post, Res } from '@nestjs/common'; +import { GenericResponse } from 'src/common/GenericResponse.model'; +import { AuthService } from './auth.service'; +import { Response } from 'express'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor(private authService: AuthService) { } + + @Post('login') + @ApiOperation({ summary: 'Login' }) + @ApiResponse({ + status: 200, + description: 'User logged in successfully', + }) + @ApiResponse({ + status: 404, + description: 'User not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "User not found" + }, + "data": null + } + }) + async login(@Body() userDto: { email: string; password: string }, @Res() res: Response) { + const user = await this.authService.validateUser(userDto); + if (!user) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'User not found' + }, null); + res.status(404).send(errorResponse); + } + const tokens = await this.authService.login(user); + const httpResponse = new GenericResponse(null, tokens); + res.status(200).send(httpResponse); + } + + @Post('refresh') + async refresh(@Body() body: { refresh_token: string }, @Res() res: Response) { + const newToken = await this.authService.refreshAccessToken(body.refresh_token); + // console.log("new token is",newToken); + if (!newToken) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Token not found' + }, null) + res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, newToken); + res.status(201).send(httpResponse); + } + + @Delete('logout') + delete(@Body() body: { refresh_token: string }, @Res() res: Response) { + const deleteToken = this.authService.logout(body.refresh_token); + res.status(200).send(deleteToken); + } + + @Post('verifyAccess') + async verifyAccessToken(@Body() body: { access_token: string }, @Res() res: Response) { + const token = await this.authService.verifyAccessToken(body.access_token); + if (token) { + res.status(200).send(token); + } + else { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Token not valid' + }, null) + res.status(404).send(errorResponse); + } + } + + @Post('verifyRefresh') + async verifyRefreshToken(@Body() body: { refresh_token: string }, @Res() res: Response) { + const token = await this.authService.verifyRefreshToken(body.refresh_token); + if (token) { + res.status(200).send(token); + } + else { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Token not valid' + }, null) + res.status(404).send(errorResponse); + } + } + +} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..0355046 --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,33 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { PassportModule } from '@nestjs/passport'; +import { JwtModule } from '@nestjs/jwt'; +import { UserModule } from 'src/user/user.module'; +import { JwtStrategy } from 'src/jwt/jwt.strategy'; +import { Utility } from 'src/common/Utility'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +@Module({ + imports: [ + // Import ConfigModule to provide ConfigService + ConfigModule.forRoot(), // Ensure this is added to load environment variables + + PassportModule.register({ defaultStrategy: 'jwt' }), + JwtModule.registerAsync({ + imports: [ConfigModule], // Import ConfigModule here + useFactory: async (configService: ConfigService) => ({ + secret: Utility.jwtConfig?.accessToken?.secretOrKey, + signOptions: { + expiresIn: Utility.jwtConfig?.accessToken?.expiresIn, + }, + }), + inject: [ConfigService], // Inject ConfigService to access environment variables + }), + UserModule, + ], + controllers: [AuthController], + providers: [AuthService, JwtStrategy], + exports: [AuthService], +}) +export class AuthModule {} diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts new file mode 100644 index 0000000..4e757ef --- /dev/null +++ b/src/auth/auth.service.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { Utility } from 'src/common/Utility'; +import JwtPayload from 'src/jwt/jwt-payload.dto'; +import RefreshToken from 'src/jwt/refresh-token.entity'; +import { UserService } from 'src/user/user.service'; + +@Injectable() +export class AuthService { + constructor(private userService: UserService, private jwtService: JwtService) { } + + private signToken(payload: any, type: 'accessToken' | 'refreshToken'): string { + console.log("yav type andre", type) + const config = Utility.jwtConfig[type]; + console.log("yav expiry andre", config.expiresIn) + return this.jwtService.sign(payload, { + secret: config.secretOrKey, + expiresIn: config.expiresIn, + }); + } + +// private signToken(payload: any, type: 'access' | 'refresh'): string { +// if (type == 'access') { +// const config = Utility.jwtConfig.accessToken. +// } + +// } + private verifyToken(token: string, type: 'accessToken' | 'refreshToken'): any { + const config = Utility.jwtConfig[type]; + try { + return this.jwtService.verify(token, { + secret: config.secretOrKey, + }); + } catch (error) { + console.log(`${type} token is invalid`, error); + return null; + } + } + + async validateUser(payload: JwtPayload) { + return this.userService.findByEmail(payload.email); + } + + async login(user: any) { + const payload: JwtPayload = { email: user.email, password: user.password }; + console.log("illig bandu nilthu", payload) + const accessToken = this.signToken(payload, 'accessToken'); + console.log("illig bandu nilthu", accessToken) + + const refreshToken = this.signToken(payload, 'refreshToken'); + + await RefreshToken.create({ email: user.email, token: refreshToken }); + + return { + access_token: accessToken, + refresh_token: refreshToken, + }; + } + + async refreshAccessToken(refreshToken: string) { + const payload = this.verifyToken(refreshToken, 'refreshToken'); + if (!payload) { + throw new Error('Invalid refresh token'); + } + + console.log(refreshToken); + console.log(payload); + const user = await this.userService.findByEmail(payload.email); + if (!user) { + throw new Error('User not found'); + } + console.log(user) + const accessToken = this.signToken({ + email: payload.email + }, 'accessToken'); + console.log(accessToken) + return { access_token: accessToken }; + } + + async verifyRefreshToken(refreshToken: string) { + const payload = this.verifyToken(refreshToken, 'refreshToken'); + if (payload) { + console.log("Refresh token is valid", payload); + } + return payload; + } + + async verifyAccessToken(accessToken: string) { + const payload = this.verifyToken(accessToken, 'accessToken'); + if (payload) { + console.log("Access token is valid", payload); + } + return payload; + } + + async logout(refreshToken: string) { + return RefreshToken.destroy({ where: { token: refreshToken } }); + } +} diff --git a/src/common/Utility.ts b/src/common/Utility.ts index 3ee458f..dda0bcc 100644 --- a/src/common/Utility.ts +++ b/src/common/Utility.ts @@ -5,7 +5,8 @@ export class Utility { static appPort: number = 3000; static models: any; static swaggerConfig: any; - static fileConfig: any = {"storagePath": "./uploads"}; + static jwtConfig: any; + static fileConfig: any = { "storagePath": "./uploads" }; static mailConfig: any = { "transport": { "host": "smtppro.zoho.in", diff --git a/src/common/common.service.ts b/src/common/common.service.ts index becfc6f..7f6bb5a 100644 --- a/src/common/common.service.ts +++ b/src/common/common.service.ts @@ -8,6 +8,7 @@ export class CommonService { fileConfig: any; mailConfig: any; swaggerConfig: any; + jwtConfig: any; constructor() { } diff --git a/src/jwt/jwt-auth.guard.ts b/src/jwt/jwt-auth.guard.ts new file mode 100644 index 0000000..2155290 --- /dev/null +++ b/src/jwt/jwt-auth.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') {} diff --git a/src/jwt/jwt-payload.dto.ts b/src/jwt/jwt-payload.dto.ts new file mode 100644 index 0000000..2b51567 --- /dev/null +++ b/src/jwt/jwt-payload.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export default class JwtPayload { + @ApiProperty({ type: String, description: 'User email' }) + email: string; + + @ApiProperty({ type: String, description: 'User password' }) + password: string; +} + diff --git a/src/jwt/jwt.strategy.ts b/src/jwt/jwt.strategy.ts new file mode 100644 index 0000000..adccd71 --- /dev/null +++ b/src/jwt/jwt.strategy.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy, ExtractJwt } from 'passport-jwt'; +import { AuthService } from 'src/auth/auth.service'; +import JwtPayload from './jwt-payload.dto'; +import { Utility } from 'src/common/Utility'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(private authService: AuthService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + secretOrKey: Utility.jwtConfig.accessToken.secretOrKey, + }); + } + + async validate(payload: JwtPayload) { + return this.authService.validateUser(payload); + } +} diff --git a/src/jwt/refresh-token.entity.ts b/src/jwt/refresh-token.entity.ts new file mode 100644 index 0000000..152e35b --- /dev/null +++ b/src/jwt/refresh-token.entity.ts @@ -0,0 +1,14 @@ +import { Table, Column, Model, DataType, Unique } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'refresh_tokens', paranoid: true }) +export default class RefreshToken extends Model { + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT }) + email: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT }) + token: string; +} diff --git a/src/main.ts b/src/main.ts index 408efe4..9b177cb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ async function bootstrap() { Utility.appPort = configMaster.local.appConfig.port; Utility.mailConfig = configMaster.local.mailConfig; Utility.fileConfig = configMaster.local.fileConfig; + Utility.jwtConfig = configMaster.local.jwtConfig; Utility.swaggerConfig = configMaster.local.swaggerConfig; const app = await NestFactory.create(AppModule, { cors: true }); diff --git a/src/user/user.module.ts b/src/user/user.module.ts index dd28343..ef764cd 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -9,6 +9,7 @@ import { UserAdditionalDetailsService } from './user-additional-details/user-add @Module({ controllers: [UserController, UserTypesController, UserAdditionalDetailsController], providers: [UserService, UserTypesService, UserAdditionalDetailsService], + exports: [UserService, UserTypesService, UserAdditionalDetailsService], imports: [] }) export class UserModule { } diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 80783f2..69f8180 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -14,10 +14,14 @@ export class UserService { return User.findByPk(id, {include: UserAdditionalDetail}) } - findOne(user: User): Promise { + findOne(user: any): Promise { return User.findOne({where: user as any, include: UserAdditionalDetail}) } + findByEmail(email: string): Promise { + return User.findOne({where: {email: email}}) + } + filter(user: User) : Promise { return User.findAll({where: user as any, include: UserAdditionalDetail}) }