I am trying to make use of the AuthGuard
decorator, and the passport JWT strategy, following the documentation.
Everything in the documentation works great.
When you look at the code of the AuthGuard, it seems like the options.callback
function is the only possible customization.
I think instead of writing your own AuthGuard
that supports scope checks, it is cleaner to have a ScopesGuard
(or RolesGuard
) with its own decorater like @Scopes('manage_server')
instead. For this, you can just follow the RolesGuard
example in the docs, which also just checks an attribute of the JWT payload under the user
property in the request.
Create a @Scopes()
decorator:
export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes);
Create a ScopesGuard
:
@Injectable()
export class ScopesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const scopes = this.reflector.get('scopes', context.getHandler());
if (!scopes) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasScope = () => user.scopes.some((scope) => scopes.includes(scope));
return user && user.scopes && hasScope();
}
}
Use the ScopesGuard as a global guard for all routes (returns true when no scopes are given):
@Module({
providers: [
{
provide: APP_GUARD,
useClass: ScopesGuard,
},
],
})
export class ApplicationModule {}
And then use it on an endpoint:
@Get('protected')
@UseGuards(AuthGuard('jwt'))
@Scopes('manage_server')
async protected(): Promise {
return 'Hello Protected World';
}