so I have a nestjs application with session authentication that I set to session by role. Then I followed one of other people's tutorials or repositories like this:
https://github.com/vladwulf/cwv-nestjs-rbac-tutorial/blob/main/apps/api
Then I get an error like this:
AccessControlError: Invalid role(s): []
I set the role based on the data in the database to make it more dynamic
and this is how I configured my security
local.strategy.ts
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(
@Inject('AUTH_SERVICE') private authService: AuthService,
@Inject('ROLE_REPOSITORY') private readonly roleRepository: RoleRepository
){
super({
usernameField: 'username'
});
}
async validate(username: string, password: string): Promise<any>{
const user = await this.authService.validateUser(username, password);
if(!user) {
throw new UnauthorizedException("Username or password isn't valid.");
}
const isAdmin = user.role === this.findRole("ADMIN");
const isOther = user.role === this.findRole("CREATOR");
let userRole = await this.roleRepository.findRoleByName("USER");
if(isAdmin) userRole = await this.roleRepository.findRoleByName("ADMIN");
if(isOther) userRole = await this.roleRepository.findRoleByName("CREATOR");
console.log({
id: user.id,
username: user.username,
role: userRole
})
return {
id: user.id,
username: user.username,
role: userRole
};
}
async findRole(roleName: string): Promise<Roles>{
const role = await this.roleRepository.findRoleByName(roleName);
if(!role) throw new DataNotFoundException("Role not found.", 400);
return role;
}
}
session.serializer.ts
export class SessionSerializer extends PassportSerializer {
constructor(
@Inject('USER_SERVICE') private userService: UserService
){
super();
}
serializeUser(user: User, done: (err: Error, user: User) => void) {
done(null, user);
}
async deserializeUser(payload: User, done: Function) {
const userDB = await this.userService.findByUsername(payload.username);
if(!userDB) {
return done(
`Could not deserialize user: user with ${payload.username} coundn't be found.`,
null
);
}
done(null, userDB);
}
}
local.guard.ts
@Injectable()
export class LocalGuard extends AuthGuard('local') {
constructor(){
super();
}
async canActivate(context: ExecutionContext){
const result = (await super.canActivate(context)) as boolean;
await super.logIn(context.switchToHttp().getRequest());
return result;
}
}
authenticated.guard.ts
@Injectable()
export class AuthenticatedGuard implements CanActivate {
canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {
const req = context.switchToHttp().getRequest<Request>();
return req.isAuthenticated();
}
}
auth.service.ts
@Injectable()
export class AuthService implements IAuthService {
constructor(
@Inject('USER_SERVICE') private userService: UserService,
@Inject('USER_REPOSITORY') private readonly userRepsitory: UserRepository,
@Inject('ROLE_REPOSITORY') private readonly roleRepository: RoleRepository
){}
public async validateUser(username: string, password: string): Promise<any> {
const user: User = await this.userService.findByUsername(username);
const isPasswordMatch: boolean = await comparePassword(password, user.password);
if(user && isPasswordMatch){
const {password, ...rest} = user;
return rest;
}
throw new ForbiddenException();
}
rbac.policy.ts
import { RolesBuilder } from "nest-access-control";
export const RBAC_POLICY: RolesBuilder = new RolesBuilder();
//define rbac policy
RBAC_POLICY
.grant('USER')
.readOwn('userData')
.createOwn('userData')
.updateOwn('userData')
.grant('CREATOR')
.extend("USER")
.read('creatorPosts')
.create('creatorPosts')
.update('creatorPosts')
.delete('creatorPosts')
.grant('ADMIN')
.extend('CREATOR')
.read('creatorData')
.delete('creatorData')
.deny('ADMIN')
.create('creatorPosts')
.update('creatorPosts')
config.module.ts
@Module({
imports: [
PassportModule.register({
session: true,
}),
ConfigModule.forRoot(
{
envFilePath: '.env',
isGlobal: true
}
),
AccessControlModule.forRoles(RBAC_POLICY)
]
})
export class ConfigurationAppModule {}
and then I tryna hit this API:
@UseRoles({
resource: 'userData',
action: 'read',
possession: 'own'
})
@UseGuards(AuthenticatedGuard)
@Get('index')
public async index(
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number = 1,
@Query('size', new DefaultValuePipe(5), ParseIntPipe) limit: number,
@Req() request: Request
): Promise<Pagination<User>>{
const options: IPaginationOptions = {
limit: limit,
page: page,
route: request.url
};
return await this.userService.index(options);
}
Got error roles not know from access control. It makes me so confused why does the role not known by access control, even though I already wrote it in rbac.policy.ts
Is the rbac policy is false? or any else?