From e52c9acf2d1a6ee4bfa7b697b64fc4af57a6c060 Mon Sep 17 00:00:00 2001 From: M1000fr Date: Thu, 12 Dec 2024 22:06:35 +0100 Subject: [PATCH] feat: remove unused packages, config --- package.json | 2 +- src/app.module.ts | 15 +----- src/config/env.ts | 19 ++----- src/modules/auth/auth.module.ts | 3 +- src/modules/auth/auth.service.ts | 74 +++++++++++++++------------- src/modules/auth/guards/jwt.guard.ts | 42 +++------------- src/modules/class/class.module.ts | 5 +- src/modules/user/user.gateway.ts | 13 +++-- src/modules/user/user.module.ts | 4 +- src/modules/user/user.service.ts | 5 +- src/validations/env.validation.ts | 1 + yarn.lock | 17 +------ 12 files changed, 74 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index dc53d87..12bbf37 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.0.0", - "@nestjs/jwt": "^10.2.0", "@nestjs/mapped-types": "^2.0.6", "@nestjs/platform-express": "^10.4.11", "@nestjs/platform-socket.io": "^10.4.12", @@ -34,6 +33,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "joi": "^17.13.3", + "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.1.0", "nestjs-prisma": "^0.23.0", "prisma": "^6.0.1", diff --git a/src/app.module.ts b/src/app.module.ts index 848c1cc..49a3bd2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,7 +1,6 @@ import env from "@Config/env"; import { Module } from "@nestjs/common"; -import { ConfigModule, ConfigService } from "@nestjs/config"; -import { JwtModule } from "@nestjs/jwt"; +import { ConfigModule } from "@nestjs/config"; import { envValidation } from "@Validations/env.validation"; import { AuthModule } from "@Modules/auth/auth.module"; @@ -17,16 +16,6 @@ import { ClassModule } from "./modules/class/class.module"; load: [env], validationSchema: envValidation, }), - JwtModule.registerAsync({ - global: true, - inject: [ConfigService], - useFactory: async (configService: ConfigService) => ({ - secret: configService.get("JWT.secret"), - signOptions: { - expiresIn: configService.get("JWT.expiresIn"), - }, - }), - }), PrismaModule.forRoot({ isGlobal: true, }), @@ -34,6 +23,6 @@ import { ClassModule } from "./modules/class/class.module"; AuthModule, ClassModule, ], - controllers: [AppController], + controllers: [AppController] }) export class AppModule {} diff --git a/src/config/env.ts b/src/config/env.ts index 9fec3e3..1b73167 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -1,19 +1,6 @@ export default () => ({ - JWT: { - secret: process.env.JWT_SECRET, - expiresIn: process.env.JWT_EXPIRES_IN, - refresh: { - secret: process.env.REFRESH_JWT_SECRET, - expiresIn: process.env.REFRESH_JWT_EXPIRES_IN, - }, - }, - oauth2: { - authorizationURL: process.env.OAUTH2_AUTHORIZATION_URL, - tokenURL: process.env.OAUTH2_TOKEN_URL, - clientID: process.env.OAUTH2_CLIENT_ID, - clientSecret: process.env.OAUTH2_CLIENT_SECRET, - callbackURL: process.env.OAUTH2_CALLBACK_URL, - profileURL: process.env.OAUTH2_PROFILE_URL, - scopes: process.env.OAUTH2_SCOPES, + auth: { + jwksURL: process.env.AUTH_JWKS_URI, + usernameField: process.env.AUTH_USERNAME_FIELD }, }); diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 45b394c..6e205a9 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -1,9 +1,8 @@ -import { UserModule } from "@Modules/user/user.module"; import { Module } from "@nestjs/common"; import { AuthService } from "./auth.service"; @Module({ - imports: [UserModule], providers: [AuthService], + exports: [AuthService], }) export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 9cb112c..9753d40 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,41 +1,49 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, UnauthorizedException } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; -import { JwtService } from "@nestjs/jwt"; +import * as jwt from "jsonwebtoken"; +import JwksRsa, * as jwksRsa from "jwks-rsa"; @Injectable() export class AuthService { - constructor( - private readonly jwtService: JwtService, - private readonly configService: ConfigService, - ) {} + private jwksClient: JwksRsa.JwksClient; - accessToken(user: { id: string }) { - return this.jwtService.sign( - { id: user.id }, - { - secret: this.configService.get("JWT.secret"), - expiresIn: this.configService.get("JWT.expiresIn"), - }, - ); - } - - refreshToken(user: { id: string }) { - return this.jwtService.sign( - { id: user.id }, - { - secret: this.configService.get("JWT.refresh.secret"), - expiresIn: this.configService.get( - "JWT.refresh.expiresIn", - ), - }, - ); - } - - async verifyToken(token: string): Promise<{ - id: string; - }> { - return await this.jwtService.verifyAsync(token, { - secret: this.configService.get("JWT.secret"), + constructor(configService: ConfigService) { + this.jwksClient = jwksRsa({ + jwksUri: configService.get("AUTH_JWKS_URI"), + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 10, }); } + + async getSigningKey(kid: string): Promise { + const key = await this.jwksClient.getSigningKey(kid); + return key.getPublicKey(); + } + + decodeJwt(token: string) { + return jwt.decode(token, { complete: true }); + } + + verifyJwt(token: string, key: string) { + return jwt.verify(token, key, { + algorithms: ["RS256"], + }); + } + + async checkToken(token: string): Promise { + const decodedHeader = this.decodeJwt(token); + const kid = decodedHeader?.header?.kid; + + if (!kid) throw "Token kid not found"; + + const key = await this.getSigningKey(kid); + + const jwtPayload = this.verifyJwt(token, key); + + if (typeof jwtPayload == "string") + throw new UnauthorizedException("Invalid token"); + + return jwtPayload; + } } diff --git a/src/modules/auth/guards/jwt.guard.ts b/src/modules/auth/guards/jwt.guard.ts index 07da1aa..ffed5cc 100644 --- a/src/modules/auth/guards/jwt.guard.ts +++ b/src/modules/auth/guards/jwt.guard.ts @@ -5,25 +5,16 @@ import { Injectable, UnauthorizedException, } from "@nestjs/common"; +import { AuthService } from "../auth.service"; import { ConfigService } from "@nestjs/config"; -import * as jwt from "jsonwebtoken"; -import * as jwksRsa from "jwks-rsa"; @Injectable() export class JwtAuthGuard implements CanActivate { - private jwksClient: jwksRsa.JwksClient; - constructor( private readonly userService: UserService, - configService: ConfigService, - ) { - this.jwksClient = jwksRsa({ - jwksUri: configService.get("AUTH_JWKS_URI"), - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 10, - }); - } + private readonly authService: AuthService, + private readonly configService: ConfigService, + ) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); @@ -34,25 +25,11 @@ export class JwtAuthGuard implements CanActivate { } try { - const decodedHeader: any = jwt.decode(token, { complete: true }); - const kid = decodedHeader?.header?.kid; - - if (!kid) { - throw new UnauthorizedException("Token kid not found"); - } - - const key = await this.getSigningKey(kid); - - const verifiedToken = jwt.verify(token, key, { - algorithms: ["RS256"], - }); - - if (typeof verifiedToken == "string") - throw new UnauthorizedException("Invalid token"); + const jwtPayload = await this.authService.checkToken(token); let user = await this.userService.findOrCreateByProviderId({ - providerId: verifiedToken.sub.toString(), - username: verifiedToken.preferred_username, + providerId: jwtPayload.sub.toString(), + username: jwtPayload[this.configService.get("auth.usernameField")], }); request.user = user; @@ -74,9 +51,4 @@ export class JwtAuthGuard implements CanActivate { return parts[1]; } - - private async getSigningKey(kid: string): Promise { - const key = await this.jwksClient.getSigningKey(kid); - return key.getPublicKey(); - } } diff --git a/src/modules/class/class.module.ts b/src/modules/class/class.module.ts index 8cd4f7a..ef454a4 100644 --- a/src/modules/class/class.module.ts +++ b/src/modules/class/class.module.ts @@ -1,10 +1,11 @@ import { Module } from "@nestjs/common"; import { ClassService } from "./class.service"; import { ClassController } from "./class.controller"; -import { UserService } from "../user/user.service"; +import { UserModule } from "../user/user.module"; @Module({ + imports: [UserModule], controllers: [ClassController], - providers: [ClassService, UserService], + providers: [ClassService], }) export class ClassModule {} diff --git a/src/modules/user/user.gateway.ts b/src/modules/user/user.gateway.ts index a3823ce..ab49037 100644 --- a/src/modules/user/user.gateway.ts +++ b/src/modules/user/user.gateway.ts @@ -9,12 +9,14 @@ import { import { Server, Socket } from "socket.io"; import { AuthService } from "../auth/auth.service"; import { UserService } from "./user.service"; +import { ConfigService } from "@nestjs/config"; @WebSocketGateway() export class UserGateway implements OnGatewayConnection, OnGatewayDisconnect { constructor( private readonly authService: AuthService, private readonly userService: UserService, + private readonly configService: ConfigService, ) {} @WebSocketServer() io: Server; @@ -22,16 +24,21 @@ export class UserGateway implements OnGatewayConnection, OnGatewayDisconnect { public clients: Socket[] = []; async handleConnection(client: Socket) { - const Authorization = client.handshake.headers.authorization; + const token = client.handshake.headers.authorization; try { - var jwtDecoded = await this.authService.verifyToken(Authorization); + var jwtPayload = await this.authService.checkToken(token); + + if (!jwtPayload) throw "Invalid token"; } catch (error) { client.emit("auth", error); return client.disconnect(); } - const user = await this.userService.findOne(jwtDecoded.id); + const user = await this.userService.findOrCreateByProviderId({ + providerId: jwtPayload.sub.toString(), + username: jwtPayload[this.configService.get("auth.usernameField")], + }); if (!user) { client.emit("auth", "User not found"); diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index b5e0a19..0795d47 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -7,8 +7,8 @@ import { UserGateway } from "./user.gateway"; import { UserService } from "./user.service"; @Module({ - providers: [UserService, PrismaService, UserGateway, AuthService], + providers: [UserService, AuthService, PrismaService, UserGateway], controllers: [UserController], - exports: [UserService], + exports: [UserService, AuthService], }) export class UserModule {} diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 8aa2809..0885d93 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -52,10 +52,9 @@ export class UserService { providerId: string; username: string; }) { - let user = await this.prisma.user.findUnique({ + let user = await this.prisma.user.findFirst({ where: { - providerId, - username, + OR: [{ providerId }, { username }], }, }); diff --git a/src/validations/env.validation.ts b/src/validations/env.validation.ts index 67db6cd..5e2cf7c 100644 --- a/src/validations/env.validation.ts +++ b/src/validations/env.validation.ts @@ -4,6 +4,7 @@ export const envValidation = Joi.object({ DATABASE_URL: Joi.string().required(), AUTH_JWKS_URI: Joi.string().uri().required(), + AUTH_USERNAME_FIELD: Joi.string().required(), PORT: Joi.number().optional(), }); diff --git a/yarn.lock b/yarn.lock index 2a811f4..2104f4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -318,14 +318,6 @@ path-to-regexp "3.3.0" tslib "2.8.1" -"@nestjs/jwt@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/jwt/-/jwt-10.2.0.tgz#6aa35a04922d19c6426efced4671620f92e6dbd0" - integrity sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g== - dependencies: - "@types/jsonwebtoken" "9.0.5" - jsonwebtoken "9.0.2" - "@nestjs/mapped-types@2.0.6", "@nestjs/mapped-types@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e" @@ -617,13 +609,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@types/jsonwebtoken@9.0.5": - version "9.0.5" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588" - integrity sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA== - dependencies: - "@types/node" "*" - "@types/jsonwebtoken@^9.0.2": version "9.0.7" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz#e49b96c2b29356ed462e9708fc73b833014727d2" @@ -2301,7 +2286,7 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonwebtoken@9.0.2: +jsonwebtoken@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==