Reputation: 451
With the help of Guards/Decorators I try to check a JWT first and then the roles a user has.
I have read the documentation regarding Authentication, Guards and Decorators and understand the principles behind them.
However, what I cannot do is to somehow make the authenticated user from JWT-Guard available to Roles-Guards.
In every example that I found, exactly this part that is interesting for me is skipped / left out...
Grateful for every tip!
This is my latest try:
jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { JwtPayload } from './jwt.model';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
passReqToCallback: true,
ignoreExpiration: false,
secretOrKey: '0000',
expiresIn: '3 days'
});
}
async validate(payload: JwtPayload) {
return {
id: payload.id,
email: payload.email,
username: payload.username
};
}
}
roles.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {
}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) {
return false;
}
const request = context.switchToHttp().getRequest();
const user = request.user ??? // THIS is what is missing
return roles.some((role) => {
return role === user.role;
});
}
}
roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
users.controller.ts
@UseGuards(AuthGuard('jwt'))
@Roles('admin', 'member')
@Get('/')
async doSomething(@Req() req): Promise<User> {
return await this.usersService.doSomething(req.user.id);
}
Upvotes: 2
Views: 10342
Reputation: 432
Your decorator and guards look fine, but from the snippet of your users.controller.ts
file it is not clear whether the roles guard is actually applied for the GET /
route.
I do, however, have an NestJS app with a quite similar setup based on the guards documentation. The following code in users.controller.ts
works as intended:
@UseGuards(JwtAuthGuard, RolesGuard)
@Controller('/users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
@Roles(UserRole.ADMIN)
public async index(): Promise<User[]> {
return this.userService.findAll();
}
// ...
}
Note how both the auth and roles guard are activated in the same scope and that JwtAuthGuard
is added before RolesGuard
. If I were to change the sequence of the guards then the RolesGuard
would not be able to retrieve the user of the request.
Also, you might want to have a look at a similar question from some time ago which contains some details on the order of guards in different scopes.
Upvotes: 7