Reputation: 9
I'm using an AuthGuard component globally in my NestJS application to protect routes, while allowing some routes to be public using a Public decorator. However, the guard isn't functioning as expected, because I can send requests to any route, even those without the Public decorator, without facing any restrictions.
authGuard
import { JwtService } from '@nestjs/jwt';
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './utils';
import { jwtConstants } from './constants';
import { Request } from 'express';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private reflector: Reflector,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException();
}
try {
request['user'] = await this.jwtService.verifyAsync(token, {
secret: jwtConstants.secret,
});
} catch {
throw new UnauthorizedException();
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
Public decorator
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
app.module
providers: [
AppService,
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
controller expample
@Post()
@UseInterceptors(
FileInterceptor('image', {
storage: diskStorage({
destination: './uploads/news',
filename: fileName,
}),
}),
)
I tryed to put authguard itself to the routes which i want to protect, but this method didnt work too
Upvotes: 0
Views: 66
Reputation: 38
My suggestion is you to wrap your AuthGuard to Decorator
export function AuthUserGuard(): MethodDecorator {
return applyDecorators(UseGuards(AuthGuard));
}
Then you can use this guard in you specific APIs where you need. For example
@AuthUserGuard()
@Post('/create')
async create(@Body() customerData: CustomerCreateDto) {
try {
//Your Business Logic
} catch (error) {
throw error;
}
}
Only use this decorator where you need a valid logged in user.
Make Sure to remove
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
Upvotes: 0
Reputation: 1075
You need to define this guard as a global guard in your main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
By the way, you don't need to throw an error in your canActivate
method from AuthGuard
class, you can return a boolean value and Nest can handle the rest since your guard is implementing CanActivate
interface.
Upvotes: 0