I've created the RoleGuard by following the official NestJs documentation. However, I keep getting a 403 error even if my users have the correct Role.
app.module.ts
@Module({
imports: [
MongooseModule.forRoot(process.env.MONGO_DB_URI),
AuthModule,
UserModule,
ProductModule,
StoreModule,
OrderModule,
],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class AppModule {}
According to the documentation:
role.enum.ts
export enum Role {
BUYER = 'buyer',
SELLER = 'seller',
ADMIN = 'admin',
}
roles.decorators.ts
import { SetMetadata } from '@nestjs/common';
import { Role } from './role.enum';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role } from './role.enum';
import { ROLES_KEY } from './roles.decorators';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
I called a route with @Roles(Role.BUYER)
and logged the user and user.roles to get { userId: undefined, email: '[email protected]' }
and undefined
.
My user entity:
user.entity.ts
import { Document } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Role } from 'src/auth/roles/role.enum';
export type UserDocument = UserEntity & Document;
@Schema({
collection: 'users',
timestamps: true,
versionKey: false,
})
export class UserEntity {
@Prop({ required: true })
username: String;
@Prop({ required: true })
password: String;
@Prop({ required: true })
email: String;
@Prop({ required: true })
telephone: String;
@Prop({ required: true, type: String, enum: Role, default: Role.BUYER })
roles: Role[];
@Prop()
storeId: String;
}
export const UserSchema = SchemaFactory.createForClass(UserEntity);
EDIT:
jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret,
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}
Your JwtStrategy
does not return a roles
property, so req.user.roles
will always be undefined
. You need to return a roles
property as a part of the JwtStrategy#validate
method to read req.user.roles
later on