Search code examples
websocketdependency-injectionservicenestjsadapter

NestJS Dependency Injection in Websocket Adapter


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?


Solution

  • 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.