6

I have implemented a jwt authentication in nestJs. However whenever I attempt to authenticate using the following authorization headers:

Bearer <token> or JWT <token>

I got 401. These are my authentication files

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
  constructor(private readonly authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: `${process.env.SECRET}`,
    });
  }

  async validate(payload: Credentials) {
    const user: Account = await this.authService.validateAccount(payload);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}


@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }

  handleRequest(err, user, info) {
    if (err || !user) {
      throw err || new UnauthorizedException();
    }
    return user;
  }
}

and this my auth module

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secretOrPrivateKey: `${process.env.SECRET}`,
    }),
    AccountModule,
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
  exports: [PassportModule, AuthService],
})
export class AuthModule {

}
Arnold Schrijver
  • 3,588
  • 3
  • 36
  • 65
Arsene
  • 1,037
  • 4
  • 20
  • 47

6 Answers6

6

validate will only be called when you pass a valid jwt token. When the token is signed with a different secret or is expired, validate will never be called. Make sure you have a valid token. You can check your token for example with the jwt debugger.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • 1
    The token is valid. However I am still getting 401. How can I know where the issue in the nestjs pipeline? – Arsene Apr 25 '19 at 23:21
  • You already extended the `AuthGuard`, so you can add `console.log` statements to its methods. You can also set breaking points in the library code and debug. – Kim Kern Apr 27 '19 at 14:07
  • Since you seem to be using pretty much the standard setup, I don't think so, no. And as you're saying your token is not expired and is signed with the right secret, it could still be a faulty header format, version conflict,... – Kim Kern Apr 28 '19 at 01:39
  • Hello NestJs geeks, kindly help me fix this issue. I have been battling with this for a while now. No solutions. – Arsene May 02 '19 at 10:13
  • You have to give more information, e.g. logs etc. or create a reproducible version on https://codesandbox.io – Kim Kern May 02 '19 at 10:32
  • Does anyone mind looking at my code because I really do not know what to do? Maybe I am missing something. – Arsene May 03 '19 at 12:43
2

I was facing similar issue, the nestjs passport jwt module was working perfectly on my local machine but was not working on the live server. After researching half a day i found that my token header was not reaching the live server, the reason for that is that i am using nginx (proxy_pass) on live server and my header field name was "access_token" so for some reason nginx removed it.

Make a simple global middle-ware and check whether you are receiving the token in the header.

Hope that helps someone.

All2Pie
  • 310
  • 3
  • 13
1

i was stuck in the same problem. Here my code (working) for you to compare:

src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { UserModule } from 'src/user/user.module';
import { PassportModule } from '@nestjs/passport';

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secretOrPrivateKey: 'secretKey',
      signOptions: {
        expiresIn: '1d',
      },
    }),
    UserModule,
  ],
  providers: [AuthService, JwtStrategy],
  exports: [PassportModule, AuthService],
})
export class AuthModule {}

src/auth/auth.service.ts

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { JwtPayload } from './interfaces/jwt-payload.interface';
import { UserService } from 'src/user/user.service';

@Injectable()
export class AuthService {
  constructor(
    private readonly jwtService: JwtService,
    private readonly userService: UserService,
  ) {}

  makeToken(payload: JwtPayload) {
    const { email } = payload;
    return this.jwtService.sign({ email });
  }

  checkToken(token: string) {
    return this.jwtService.verify(token);
  }

  async validateUser(payload: JwtPayload) {
    return await this.userService.read(payload.email);
  }
}

src/auth/jwt.strategy.ts

import { Strategy, ExtractJwt, VerifiedCallback } from 'passport-jwt';
import { AuthService } from './auth.service';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtPayload } from './interfaces/jwt-payload.interface';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'secretKey',
    });
  }

  async validate(payload: JwtPayload, done: VerifiedCallback) {
    const user = await this.authService.validateUser(payload);
    if (!user) {
      done(new UnauthorizedException(), false);
    }
    return done(null, user);
  }
}

src/auth/interfaces/jwt-payload.interface.ts

export interface JwtPayload {
  email: string;
}

src/account/account.module.ts

import { Module } from '@nestjs/common';
import { AccountController } from './account.controller';
import { PassportModule } from '@nestjs/passport';
import { AuthModule } from 'src/auth/auth.module';

@Module({
  imports: [AuthModule],
  controllers: [AccountController],
})
export class AccountModule {}

src/account/account.controller.ts

import { Controller, UseGuards, Post } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('account')
export class AccountController {
  @Post('skills')
  @UseGuards(AuthGuard())
  updateSkills() {
    return console.log('something');
  }
}

P.S.: I did not do a JwtAuthGuard.

I hope it helped you :)

0

You can view a minimum working example with passport and NestJS

https://github.com/leosuncin/nest-auth-example

Main differences with your code:

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }), // I don't do this because I explicity call every stratategy
    JwtModule.register({
      secretOrPrivateKey: 'secretKey',
      signOptions: {
        expiresIn: '1d',
      },
    }),
    UserModule,
  ],
  providers: [AuthService, JwtStrategy],
  exports: [PassportModule, AuthService], // I don't do this
})

I don't use any JwtAuthGuard just use the default one.

Jaime Suncin
  • 333
  • 2
  • 8
0

I got this problem and i solved it. Just remove ${procces.env.JWT_SECRET} and do something else, async register for example, i don't know why, but it`s work.

Daniil
  • 11
  • 4
0

This might seem obvious, have you checked you're using the correct base class?

If not, you will get no errors and no feedback other than the JWT authentication simply never working:

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ExtractJwt } from 'passport-jwt';

@Injectable()
export class JwtAuthStrategy extends PassportStrategy(Strategy) {
...

^ If you are copy and pasting between files, don't do this.

Specifically, this:

import { Strategy } from 'passport-local'; <---- 

The JWT strategy must extend Strategy from the correct module, in this case:

import { Strategy } from 'passport-jwt'; <-- THIS

Since you haven't included your includes, its impossible to tell if this is your issue, but in general, your first port of call should be to ensure that you're using the correct base strategy.

Doug
  • 32,844
  • 38
  • 166
  • 222