Closed AlessandroStaffolani closed 2 weeks ago
If you want the keycloak instance, inject it using the KEYCLOAK_INSTANCE
provider
Can you provide an example of how to properly inject the instance?
I tried in the app.module
(where I register the KeycloakConnectModule
) to export the provider, but when I'm injecting the instance nest cannot resolve it.
I inject the instance like so:
import {
Injectable,
CanActivate,
ExecutionContext,
Inject,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { KEYCLOAK_INSTANCE } from 'nest-keycloak-connect';
import { Keycloak } from 'keycloak-connect';
@Injectable()
export class UserOwnedGuard implements CanActivate {
constructor(
@Inject(KEYCLOAK_INSTANCE) private readonly keycloakInstance: Keycloak,
) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.user;
const userId = request.params.userId;
return user.sid === userId;
}
}
Can you provide the error ? Also is KeycloakConnectModule
available in your root module ? Also keycloak instance type should be KeycloakConnect
:
import KeycloakConnect from 'keycloak-connect';
Here are all the details:
My app.module.ts
:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
import {
AuthGuard,
KeycloakConnectModule,
ResourceGuard,
RoleGuard,
} from 'nest-keycloak-connect';
import { KeycloakConfigService } from './config/keycloak-config/keycloak-config.service';
import { KeycloakConfigModule } from './config/keycloak-config/keycloak-config.module';
import { APP_GUARD } from '@nestjs/core';
import { TemplatesModule } from "./resources/templates/templates.module";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
}),
KeycloakConnectModule.registerAsync({
useExisting: KeycloakConfigService,
imports: [KeycloakConfigModule],
}),
TemplatesModule
],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
{
provide: APP_GUARD,
useClass: ResourceGuard,
},
{
provide: APP_GUARD,
useClass: RoleGuard,
},
],
})
export class AppModule {}
I have also changed the guard as suggested by you, but if I set as a type import KeycloakConnect from 'keycloak-connect';
for the injected instance typescript complains like so: Cannot use namespace 'KeycloakConnect' as a type.
This is the updated guard:
import {
Injectable,
CanActivate,
ExecutionContext,
Inject,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { KEYCLOAK_INSTANCE } from 'nest-keycloak-connect';
import KeycloakConnect from 'keycloak-connect';
@Injectable()
export class UserOwnedGuard implements CanActivate {
constructor(
@Inject(KEYCLOAK_INSTANCE)
private readonly KeycloakConnect: KeycloakConnect,
) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.user;
const userId = request.params.userId;
return user.sub === userId;
}
}
Finally, if I remove the type and I run it the error I get is this:
Nest can't resolve dependencies of the UserOwnedGuard (?). Please make sure that the argument KEYCLOAK_INSTANCE at index [0] is available in the TemplatesModule context.
Potential solutions:
- If KEYCLOAK_INSTANCE is a provider, is it part of the current TemplatesModule?
- If KEYCLOAK_INSTANCE is exported from a separate @Module, is that module imported within TemplatesModule?
@Module({
imports: [ /* the Module containing KEYCLOAK_INSTANCE */ ]
})
Where TemplatesModule
is the module in which I'm using the guard. In the templates-controller I use the guard like so:
import { Controller, UseGuards } from '@nestjs/common';
import { TemplatesService } from './templates.service';
import { Roles } from 'nest-keycloak-connect';
import { UserOwnedGuard } from '../../lib/userOwnedGuard';
import { KeycloakRoles } from '../../config/keycloak-config/keycloak-roles';
@Roles({ roles: [KeycloakRoles.RealmUser] })
@UseGuards(UserOwnedGuard)
@Controller('users/:userId/templates')
export class TemplatesController {
constructor(private readonly templatesService: TemplatesService) {}
}
Any updates on this?
This can now be done with the implementation of @ConditionalScopes
.
I'm trying to build a custom guard to verify that the logger user is the same asking for resources belonging to him. For example, if I have a resource
/users/alice/cats
I want to verify that alice is the current user. In addition, my custom guard should allow access to the resource if the user has roleadmin
.For the first step I simply use the user in the request and verify the param, here is the code:
However, to verify the second step I need to get the token object from the grant and call the
hasRole
method. How can I inject the keycloak instance on my guard?