Yan Zhihou
Yan Zhihou

Reputation: 11

using passport-jwt in nest with secretOrKeyProvider option keep responsing http 401?

I am a new coder with nestjs, I want to use passport-jwt , nestjs/passport and firebase to build my app's authetication part, below are my codes. But I just got http 401 response, how can i fix it ?

here is my strategy:

import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { getAuth } from 'firebase-admin/auth';

@Injectable()
export class FirebaseStrategy extends PassportStrategy(Strategy, 'firebase') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKeyProvider: async (request, rawJwtToken, done) => {
        try {
          const decodedToken = await getAuth().verifyIdToken(rawJwtToken);
          done(null, decodedToken);
        } catch (error) {
          done(error);
        }
      },
    });
  }

  async validate(payload: any) {
    console.log('validate');
    return payload;
  }
}

here is my guard:

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class FirebaseAuthGuard extends AuthGuard('firebase') {}

here is my auth.module.ts:

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { FirebaseStrategy } from './firebase.stragety';
import { AuthController } from './auth.controller';

@Module({
  controllers: [AuthController],
  imports: [UsersModule, PassportModule, JwtModule.register({})],
  providers: [AuthService, FirebaseStrategy],
  exports: [AuthService],
})
export class AuthModule {}

and here is my controller:

import { Controller, Get, Request, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { FirebaseAuthGuard } from './firebase.guard';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @UseGuards(FirebaseAuthGuard)
  @Get('login')
  async logIn(@Request() req) {
    return 'login';
  }
}

I just found my validate method in FirebaseStrategy not invoked, it should be invoked everytime when secretOrKeyProvider verified jwt in http header, isn't it ?

Upvotes: 1

Views: 2023

Answers (2)

M. Fechner
M. Fechner

Reputation: 63

The callback definition secretOrKeyProvider is used to return the secret that is required to verify the JWT. It is not intended to verify the JWT in this callback, see also here: https://www.passportjs.org/packages/passport-jwt/

The validation of the JWT should be done an a validate function, so move your await getAuth().verifyIdToken(rawJwtToken); into a validate function. I use it normally like this:

  async validate(payload: any) {
    console.log('validate');
    let user;
    if (payload) {
      this.logger.debug('Received a normal access token, check access token.');
      user = await this.usersService.validateJwtPayload(payload);
    }

    if (!user) {
      throw new UnauthorizedError();
    }
    return user;
  }

In the validateJwtPayload I e.g. lookup the user and ensure the user was not deativated in the meantime, save a lastSeenAt timestamp for the user in the database. If the user is inactivated or payload.username does not exist return there undefined which will then block access.

Upvotes: 1

Jay McDoniel
Jay McDoniel

Reputation: 70510

If you're getting a 401 with no call of the validate method of your JwtStrategy that means that for some reason passport is reading the JWT you send as invalid. To find the specific reason for it you can modify your FirebaseAuthGuard to have the following handleRequest method, which will log out extra details

handleRequest(err, user, info, context, status) {
  console.log({ err, user, info, context, status });
  return super.handleRequest(err, user, info, context, status);
}

As mentioned, this will print out what error or info passport is reading and throwing for, and will allow you to properly adjust your JWT/know to refresh it/change it because of invalid signature/whatever else.

Upvotes: 1

Related Questions