I'm trying to authenticate and check permission of a user while establishing a websocket connection in a NestJS application.
I've found this discussion which recommends to make use of NestJS Websocket adapter. You can perform the token validation in the options.allowRequest
callback as below.
export class AuthenticatedSocketIoAdapter extends IoAdapter {
private readonly authService: AuthService;
constructor(private app: INestApplicationContext) {
super(app);
this.authService = app.get(AuthService);
}
createIOServer(port: number, options?: SocketIO.ServerOptions): any {
options.allowRequest = async (request, allowFunction) => {
const token = request.headers.authorization.replace('Bearer ', '');
const verified = this.authService.verifyToken(token);
if (verified) {
return allowFunction(null, true);
}
return allowFunction('Unauthorized', false);
};
return super.createIOServer(port, options);
}
}
I have a problem however with the dependency injection in the websocket adapter. The IoAdapter
's constructor has an INestApplicationContext
parameter from which I'm trying to get back the AuthService
using app.get(AuthService)
as you can see above.
The AuthService injects two other services, a UserService
and the JwtService
to check the JWT token. My problem is that those services remained not defined in that context.
@Injectable()
export class AuthService {
constructor(private usersService: UsersService, private jwtService: JwtService) {}
verifyToken(token: string): boolean {
// Problem: this.jwtService is undefined
const user = this.jwtService.verify(token, { publicKey });
// ... check user has permissions and return result
}
For info, the AuthService
is in another module than the one which defines the Websocket. I also tried to import the AuthService (and its dependencies) in the current module but that didn't help.
Is that possible to make use the service using the app.get()
method?
I could solve the DI issue by using app.resolve()
instead of app.get()
export class AuthenticatedSocketIoAdapter extends IoAdapter {
private authService: AuthService;
constructor(private app: INestApplicationContext) {
super(app);
app.resolve<AuthService>(AuthService).then((authService) => {
this.authService = authService;
});
}
}
This solved the jwtService
injected in the AuthService
being undefined.