Skip to content

Commit

Permalink
Merge pull request #19 from C-Alexis4414/YL-36-BackSecuring-CSRFToken
Browse files Browse the repository at this point in the history
feat: YL-36-BackSecuring-CSRFToken
  • Loading branch information
accardigianni authored Oct 4, 2024
2 parents b910258 + 7884dfb commit b40f8c9
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 8 deletions.
163 changes: 163 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.6",
"csurf": "^1.10.0",
"express-rate-limit": "^7.4.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.2.0",
Expand Down
5 changes: 5 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ import { LikedModule } from './liked/liked.module';
import { AuthModule } from './authentification/auth.module';
import { TagsModule } from './tags/tag.module';
import { SubscriptionModule } from './subscription/subcription.module';
import { SecurityModule } from './security/security.module';
// import { ConfigModule } from '@nestjs/config';

@Module({
imports: [
// ConfigModule.forRoot({
// isGlobal: true,
// }),
UserModule,
CategoryModule,
LikedModule,
TagsModule,
SubscriptionModule,
AuthModule,
SecurityModule,
],
controllers: [],
providers: [],
Expand Down
8 changes: 6 additions & 2 deletions src/authentification/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//TOOLS
import { Module } from '@nestjs/common';
// import { ConfigModule, ConfigService } from '@nestjs/config';

//CONTROLLERS
import { AuthController } from './controller/auth.controller';
Expand All @@ -19,10 +20,13 @@ import { JwtStrategy } from './jwt.strategy';
controllers: [AuthController],
providers: [AuthService, PrismaService, JwtStrategy, UserService],
imports: [
JwtModule.register({
// ConfigModule,
JwtModule.register({ // JwtModule.registerAsync
// useFactory: async (configService: ConfigService) => ({
global: true,
secret: process.env.JWT_SECRET,
secret: process.env.JWT_SECRET, // secret: configService.get<string>('JWT_SECRET'),
signOptions: { expiresIn: '24h' },
// inject: [ConfigService],
}),],
})
export class AuthModule { }
7 changes: 6 additions & 1 deletion src/authentification/controller/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// TOOLS
import { Body, Controller, Post, Get, Request, UseGuards } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { ApiTags, ApiHeader } from '@nestjs/swagger';

// GUARDS
import { JwtAuthGuard } from '../jwt-auth.guard';
Expand Down Expand Up @@ -31,6 +31,11 @@ export class AuthController {
return await this.authService.login(authLogin);
}

@ApiHeader({
name: 'X-CSRF-Token',
description: 'CSRF token',
})

@Post('register')
async register(@Body() userData: CreateUserDto) {
return await this.authService.register(userData);
Expand Down
5 changes: 3 additions & 2 deletions src/authentification/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { AuthPayloadDto } from './dto/auth.dto';
import { UserPayloadType } from './type/auth.type';
// import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
constructor() { // constructor(private configService: ConfigService)
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
secretOrKey: process.env.JWT_SECRET, // secretOrKey: configService.get<string>('JWT_SECRET'),
});
}

Expand Down
13 changes: 13 additions & 0 deletions src/csrf-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ExceptionFilter, Catch, ArgumentsHost, ForbiddenException } from '@nestjs/common';

@Catch(ForbiddenException)
export class CsrfExceptionFilter implements ExceptionFilter {
catch(exception: ForbiddenException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
// Return a custom message for csrf errors
response.status(403).json({
message: 'ERROR 403 - CSRF token is invalid, please pass it in the header of the request'
});
}
}
17 changes: 17 additions & 0 deletions src/csrf.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class CsrfInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const ctx = context.switchToHttp();
const request = ctx.getRequest();

// Added a CSRF header for each query
request.headers['X-CSRF-TOKEN'] = process.env.CSRF_TOKEN;

return next.handle().pipe(tap(() => {}));
}
}

31 changes: 30 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { CsrfExceptionFilter } from './csrf-exception.filter';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand All @@ -10,6 +11,26 @@ async function bootstrap() {
.setDescription('testing API address')
.setVersion('1.0')
.addTag('links')
.addBearerAuth(
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
'authorization'
)
.addCookieAuth('csrf-token', {
type: 'apiKey',
in: 'cookie',
name: 'X-CSRF-TOKEN',
description: 'CSRF token for protection',
})
.addApiKey({
type: 'apiKey',
name: 'X-CSRF-TOKEN',
in: 'header',
description: 'CSRF protection token',
}, 'csrf-token')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
Expand All @@ -19,7 +40,15 @@ async function bootstrap() {
// scheme: 'basic',
// });

app.enableCors();
app.enableCors({
origin: 'http://localhost:3000',
credentials: true,
});

// Save global filter for csrf errors
app.useGlobalFilters(new CsrfExceptionFilter());

await app.listen(3000);

}
bootstrap()
Loading

0 comments on commit b40f8c9

Please sign in to comment.