feat: remove unused packages, config

This commit is contained in:
M1000fr 2024-12-12 22:06:35 +01:00
parent bc4dcc26ef
commit e52c9acf2d
12 changed files with 74 additions and 126 deletions

View File

@ -23,7 +23,6 @@
"@nestjs/common": "^10.0.0", "@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.3.0", "@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0", "@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/mapped-types": "^2.0.6", "@nestjs/mapped-types": "^2.0.6",
"@nestjs/platform-express": "^10.4.11", "@nestjs/platform-express": "^10.4.11",
"@nestjs/platform-socket.io": "^10.4.12", "@nestjs/platform-socket.io": "^10.4.12",
@ -34,6 +33,7 @@
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.1",
"joi": "^17.13.3", "joi": "^17.13.3",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0", "jwks-rsa": "^3.1.0",
"nestjs-prisma": "^0.23.0", "nestjs-prisma": "^0.23.0",
"prisma": "^6.0.1", "prisma": "^6.0.1",

View File

@ -1,7 +1,6 @@
import env from "@Config/env"; import env from "@Config/env";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config"; import { ConfigModule } from "@nestjs/config";
import { JwtModule } from "@nestjs/jwt";
import { envValidation } from "@Validations/env.validation"; import { envValidation } from "@Validations/env.validation";
import { AuthModule } from "@Modules/auth/auth.module"; import { AuthModule } from "@Modules/auth/auth.module";
@ -17,16 +16,6 @@ import { ClassModule } from "./modules/class/class.module";
load: [env], load: [env],
validationSchema: envValidation, validationSchema: envValidation,
}), }),
JwtModule.registerAsync({
global: true,
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>("JWT.secret"),
signOptions: {
expiresIn: configService.get<string>("JWT.expiresIn"),
},
}),
}),
PrismaModule.forRoot({ PrismaModule.forRoot({
isGlobal: true, isGlobal: true,
}), }),
@ -34,6 +23,6 @@ import { ClassModule } from "./modules/class/class.module";
AuthModule, AuthModule,
ClassModule, ClassModule,
], ],
controllers: [AppController], controllers: [AppController]
}) })
export class AppModule {} export class AppModule {}

View File

@ -1,19 +1,6 @@
export default () => ({ export default () => ({
JWT: { auth: {
secret: process.env.JWT_SECRET, jwksURL: process.env.AUTH_JWKS_URI,
expiresIn: process.env.JWT_EXPIRES_IN, usernameField: process.env.AUTH_USERNAME_FIELD
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,
}, },
}); });

View File

@ -1,9 +1,8 @@
import { UserModule } from "@Modules/user/user.module";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { AuthService } from "./auth.service"; import { AuthService } from "./auth.service";
@Module({ @Module({
imports: [UserModule],
providers: [AuthService], providers: [AuthService],
exports: [AuthService],
}) })
export class AuthModule {} export class AuthModule {}

View File

@ -1,41 +1,49 @@
import { Injectable } from "@nestjs/common"; import { Injectable, UnauthorizedException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt"; import * as jwt from "jsonwebtoken";
import JwksRsa, * as jwksRsa from "jwks-rsa";
@Injectable() @Injectable()
export class AuthService { export class AuthService {
constructor( private jwksClient: JwksRsa.JwksClient;
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
accessToken(user: { id: string }) { constructor(configService: ConfigService) {
return this.jwtService.sign( this.jwksClient = jwksRsa({
{ id: user.id }, jwksUri: configService.get<string>("AUTH_JWKS_URI"),
{ cache: true,
secret: this.configService.get<string>("JWT.secret"), rateLimit: true,
expiresIn: this.configService.get<string>("JWT.expiresIn"), jwksRequestsPerMinute: 10,
},
);
}
refreshToken(user: { id: string }) {
return this.jwtService.sign(
{ id: user.id },
{
secret: this.configService.get<string>("JWT.refresh.secret"),
expiresIn: this.configService.get<string>(
"JWT.refresh.expiresIn",
),
},
);
}
async verifyToken(token: string): Promise<{
id: string;
}> {
return await this.jwtService.verifyAsync(token, {
secret: this.configService.get<string>("JWT.secret"),
}); });
} }
async getSigningKey(kid: string): Promise<string> {
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<jwt.JwtPayload> {
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;
}
} }

View File

@ -5,25 +5,16 @@ import {
Injectable, Injectable,
UnauthorizedException, UnauthorizedException,
} from "@nestjs/common"; } from "@nestjs/common";
import { AuthService } from "../auth.service";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import * as jwt from "jsonwebtoken";
import * as jwksRsa from "jwks-rsa";
@Injectable() @Injectable()
export class JwtAuthGuard implements CanActivate { export class JwtAuthGuard implements CanActivate {
private jwksClient: jwksRsa.JwksClient;
constructor( constructor(
private readonly userService: UserService, private readonly userService: UserService,
configService: ConfigService, private readonly authService: AuthService,
) { private readonly configService: ConfigService,
this.jwksClient = jwksRsa({ ) {}
jwksUri: configService.get<string>("AUTH_JWKS_URI"),
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10,
});
}
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
@ -34,25 +25,11 @@ export class JwtAuthGuard implements CanActivate {
} }
try { try {
const decodedHeader: any = jwt.decode(token, { complete: true }); const jwtPayload = await this.authService.checkToken(token);
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");
let user = await this.userService.findOrCreateByProviderId({ let user = await this.userService.findOrCreateByProviderId({
providerId: verifiedToken.sub.toString(), providerId: jwtPayload.sub.toString(),
username: verifiedToken.preferred_username, username: jwtPayload[this.configService.get("auth.usernameField")],
}); });
request.user = user; request.user = user;
@ -74,9 +51,4 @@ export class JwtAuthGuard implements CanActivate {
return parts[1]; return parts[1];
} }
private async getSigningKey(kid: string): Promise<string> {
const key = await this.jwksClient.getSigningKey(kid);
return key.getPublicKey();
}
} }

View File

@ -1,10 +1,11 @@
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { ClassService } from "./class.service"; import { ClassService } from "./class.service";
import { ClassController } from "./class.controller"; import { ClassController } from "./class.controller";
import { UserService } from "../user/user.service"; import { UserModule } from "../user/user.module";
@Module({ @Module({
imports: [UserModule],
controllers: [ClassController], controllers: [ClassController],
providers: [ClassService, UserService], providers: [ClassService],
}) })
export class ClassModule {} export class ClassModule {}

View File

@ -9,12 +9,14 @@ import {
import { Server, Socket } from "socket.io"; import { Server, Socket } from "socket.io";
import { AuthService } from "../auth/auth.service"; import { AuthService } from "../auth/auth.service";
import { UserService } from "./user.service"; import { UserService } from "./user.service";
import { ConfigService } from "@nestjs/config";
@WebSocketGateway() @WebSocketGateway()
export class UserGateway implements OnGatewayConnection, OnGatewayDisconnect { export class UserGateway implements OnGatewayConnection, OnGatewayDisconnect {
constructor( constructor(
private readonly authService: AuthService, private readonly authService: AuthService,
private readonly userService: UserService, private readonly userService: UserService,
private readonly configService: ConfigService,
) {} ) {}
@WebSocketServer() io: Server; @WebSocketServer() io: Server;
@ -22,16 +24,21 @@ export class UserGateway implements OnGatewayConnection, OnGatewayDisconnect {
public clients: Socket[] = []; public clients: Socket[] = [];
async handleConnection(client: Socket) { async handleConnection(client: Socket) {
const Authorization = client.handshake.headers.authorization; const token = client.handshake.headers.authorization;
try { try {
var jwtDecoded = await this.authService.verifyToken(Authorization); var jwtPayload = await this.authService.checkToken(token);
if (!jwtPayload) throw "Invalid token";
} catch (error) { } catch (error) {
client.emit("auth", error); client.emit("auth", error);
return client.disconnect(); 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) { if (!user) {
client.emit("auth", "User not found"); client.emit("auth", "User not found");

View File

@ -7,8 +7,8 @@ import { UserGateway } from "./user.gateway";
import { UserService } from "./user.service"; import { UserService } from "./user.service";
@Module({ @Module({
providers: [UserService, PrismaService, UserGateway, AuthService], providers: [UserService, AuthService, PrismaService, UserGateway],
controllers: [UserController], controllers: [UserController],
exports: [UserService], exports: [UserService, AuthService],
}) })
export class UserModule {} export class UserModule {}

View File

@ -52,10 +52,9 @@ export class UserService {
providerId: string; providerId: string;
username: string; username: string;
}) { }) {
let user = await this.prisma.user.findUnique({ let user = await this.prisma.user.findFirst({
where: { where: {
providerId, OR: [{ providerId }, { username }],
username,
}, },
}); });

View File

@ -4,6 +4,7 @@ export const envValidation = Joi.object({
DATABASE_URL: Joi.string().required(), DATABASE_URL: Joi.string().required(),
AUTH_JWKS_URI: Joi.string().uri().required(), AUTH_JWKS_URI: Joi.string().uri().required(),
AUTH_USERNAME_FIELD: Joi.string().required(),
PORT: Joi.number().optional(), PORT: Joi.number().optional(),
}); });

View File

@ -318,14 +318,6 @@
path-to-regexp "3.3.0" path-to-regexp "3.3.0"
tslib "2.8.1" 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": "@nestjs/mapped-types@2.0.6", "@nestjs/mapped-types@^2.0.6":
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e" 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" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== 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": "@types/jsonwebtoken@^9.0.2":
version "9.0.7" version "9.0.7"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz#e49b96c2b29356ed462e9708fc73b833014727d2" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz#e49b96c2b29356ed462e9708fc73b833014727d2"
@ -2301,7 +2286,7 @@ jsonfile@^6.0.1:
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.6" graceful-fs "^4.1.6"
jsonwebtoken@9.0.2: jsonwebtoken@^9.0.2:
version "9.0.2" version "9.0.2"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3"
integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==