'How to get the user from request in a dynamically created custom provider in NestJS?

Purpose

Create a custom provider that returns an axios instance that has interceptors that uses the attached authenticated user (from authguarded jwt-strategy) from request.

Context

I need to consume a third party oauth2 api. So I need intercepts for before request - to add the accessToken in the header - and for after request - to validated a possible ExpiredTokenException, where I'll need to perform a refresh token and redo the original request. This third party oauth2 api has a User level authentication. So my app is as well authenticated in user level - In my nestjs app I have my routes authguarded with jwt-strategy working propertly.

Problem

Considering that the HttpModule and HttpService exported from the @nestjs/common package have been deprecated, I trying to accomplish my goal creating a dynamically custom provider that returns an axios instance. The problem is that since this third party ouath2 api is user level authenticated, in this provider I need to retrieve from my database the accessToken/RefreshToken about the user authenticated in my app, so I'm trying to get them from the request, since after authenticated by passport, it's added to the request, but it seems that the factory is ran before that the authguard, so my req.user is undefined at the time.

Do not misunderstood the auths here: I have my app authenticating using jwt-strategy and I have in my database the accessToken/RefreshToken for the user in the third party api.

So here's my code:

Jwt-strategy

@Injectable()
export class JwtAccessTokenStrategy extends PassportStrategy(
  Strategy,
  'jwt-access-token',
) {
  constructor(
    readonly configService: ConfigService,
    private moduleRef: ModuleRef,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get('jwt.accessTokenSecret'),
      passReqToCallback: true,
    });
  }

  async validate(request: Request, payload: any) {
    const contextID = ContextIdFactory.getByRequest(request);
    const usersService = await this.moduleRef.resolve(UsersService, contextID);
    const user = await usersService.findOne(payload.email);
    console.log(user);

    if (!user) throw new UnauthorizedException();
    return user;
  }
}

Controller

@Controller('gtc-me')
export class GtcMeController {
  constructor(private readonly gtcMeService: GtcMeService) {}

  @UseGuards(JwtAccessTokenAuthGuard)
  @Get()
  async me() {
    return await this.gtcMeService.me();
  }
}

Service

@Injectable()
export class GtcMeService {
  constructor(@Inject('GTC_API') private readonly gtc: AxiosInstance) {}

  async me() {
    const { data } = await this.gtc.get('https://thirdpartyapi.com/api...');
    ...
  }
}

Axios dynamically created custom provider

const gtcFactory = {
  provide: 'GTC_API',
  useFactory: async (
    gtcUsersAuthService: GtcUsersAuthService,
    req: Request,
  ) => {
    const instance = axios.create();

    console.log(req['user']);

    instance.interceptors.request.use(
      async (config) => {
        const at = (await gtcUsersAuthService.findByUserId(req['user'].id))
          .accessToken;

        config.headers = {
          Authorization: `Bearer ${at}`,
        };
        return config;
      },
      (error) => {
        Promise.reject(error);
      },
    );

    return instance;
  },
  inject: [GtcUsersAuthService, REQUEST],
};

@Module({
  imports: [GtcUsersAuthModule],
  providers: [gtcFactory],
  exports: ['GTC_API'],
})
export class GtcApiModule {}

So requesting:

http://localhost/gtc-me

Console output:

undefined --printed from gtcFactory

{user object} --printed from JwtAccessTokenStrategy

So is there another way to accomplish my purpose here? I mean, how can I get the user from request?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source