1

Sorry for my bad english, I'm from Ukraine :) Could you tell me how can I create my own service, that extends of Jwt service provided jwt module from npm package? I want to create my own JwtService for catch errors and isolate duplicate logic for token creation and verification. Please, help me how can I do it. Code samples attached.

import { BadRequestException, Injectable } from '@nestjs/common';
import { JwtService as NestJwtService, JwtVerifyOptions } from '@nestjs/jwt';

@Injectable()
export class OwnJwtService extends NestJwtService {
  constructor() {
    super({});
  }

  async verifyAsync<T>(token: string, options?: JwtVerifyOptions): Promise<T> {
    try {
      const res = await super.verifyAsync(token, options);
      console.log('res', res);
      return res;
    } catch (error) {
      // My own logic here ...
      throw new BadRequestException({
        error,
        message: 'Error with verify provided token',
      });
    }
  }
}

or maybe I need to inject nestjs jwt service to my own service ? example:

import { BadRequestException, Injectable } from '@nestjs/common';
import { JwtService as NestJwtService, JwtVerifyOptions } from '@nestjs/jwt';

@Injectable()
export class OwnJwtService {
  constructor(private readonly jwtService: NestJwtService) {}

  async verifyAsync<T>(token: string, options?: JwtVerifyOptions): Promise<T> {
    try {
      const res = await this.jwtService.verifyAsync(token, options);
      console.log('res', res);
      return res;
    } catch (error) {
      throw new BadRequestException({
        error,
        message: 'Error with verify provided token',
      });
    }
  }
}

and

import { JwtModule as NestJwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Module } from '@nestjs/common';

import { OwnJwtService } from 'src/modules/jwt/jwt.service';

@Module({
  imports: [
    NestJwtModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        signOptions: {
          expiresIn: process.env.JWT_EXPIRES_IN,
        },
        secret: process.env.JWT_SECRET,
        secretOrPrivateKey: process.env.JWT_SECRET,
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [OwnJwtService],
  exports: [OwnJwtService],
})
export class JwtModule {}

but it doesn't work for me, and I have similar errors:

Error: Nest can't resolve dependencies of the OwnJwtService (?). Please make sure that the argument JwtService at index [0] is available in the AuthModule context.

1 Answers1

0

First, notice that the JwtModule basically creates a module based on jsonwebtoken and your custom errors aren't meant to be dealt inside it.

Second, when you use registerAsync you are meant to get your ENV variables with the ConfigService as in configService.get('JWT_SECRET').

Third, your question is inefficient. The JwtModule already does everything you need. You just need to implement it. Again, just think of it as the jsonwebtoken package adapted for Nest. That's it.

On the signup, login and refreshtoken (if existing) routes you sign when you create a new token. And in your requests middleware you verify.

One kind of a big issue with Nest is its documentation. It doesn't have everything you need. There might be more than one way to verify a route, but the most straightforward is just using Express middleware, as in a typical Express app.

To do this, you need to implement it in the AppModule like this:

@Module(...)
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {
    consumer.apply(cookieParser(), AuthMiddleware).forRoutes('/');
  }
}

In this example, I'm also registering the module cookieParser() because I send the tokens in a cookie. Other cookie modules will do, too. Both the NestModule and the MiddlewareConsumer come from @nestjs/common.

AuthMiddleware is a middleware I made using this skeleton...

export class AuthMiddleware implements NestMiddleware {
  constructor(
    private readonly configService: ConfigService,
    private readonly jwtService: JwtService
  ) {}

  async use(req: Request, res: Response, next: NextFunction) {
    const { yourJwtToken } = req.cookies;
    const isValidToken = this.jwtService.verify(
      yourJwtToken,
      this.configService.get('JWT_SECRET'),
    );

    if (!isValidToken) throw new UnauthorizedException();

    // etc...

    next();
  }
}

Finally, what you might be asking to, is to apply the AuthGuard.

If you use the Passport ones, you need just to follow the documentation to apply them. They already throw errors if you. If you want to change it, just rewrite its methods.

You can also do it manually. Just use the console to generate a guard, and in there you can check authentication context.switchToHttp().getRequest() and return a boolean after checking the credentials and use the constructor to check the permissions if you want.

You might also skip the middleware config from above and implement the logic inside the guard if you will.

Again, I don't really think changing the JwtModule is the best idea here.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Rex Factorem
  • 31
  • 1
  • 3