Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Using Postman to test my endpoints, I am able to successfully "login" and receive a JWT token. Now, I am trying to hit an endpoint that supposedly has an AuthGuard to ensure that now that I am logged in, I can now access it.

However, it constantly returns 401 Unauthorized even when presented the JWT token in Postman.

Here is my code:

user.controller.ts

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}
  @UseGuards(AuthGuard())
  @Get()
  getUsers() {
    return this.usersService.getUsersAsync();

jwt.strategy.ts

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authenticationService: AuthenticationService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'SuperSecretJWTKey',
  async validate(payload: any, done: Function) {
    console.log('I AM HERE'); // this never gets called.
    const user = await this.authenticationService.validateUserToken(payload);
    if (!user) {
      return done(new UnauthorizedException(), false);
    done(null, user);

I have tried ExtractJWT.fromAuthHeaderWithScheme('JWT') as well but that does not work.

authentication.module.ts

@Module({
  imports: [
    ConfigModule,
    UsersModule,
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secret: 'SuperSecretJWTKey',
      signOptions: { expiresIn: 3600 },
  controllers: [AuthenticationController],
  providers: [AuthenticationService, LocalStrategy, JwtStrategy],
  exports: [AuthenticationService, LocalStrategy, JwtStrategy],
export class AuthenticationModule {}

authentication.controller.ts

@Controller('auth')
export class AuthenticationController {
  constructor(
    private readonly authenticationService: AuthenticationService,
    private readonly usersService: UsersService,
  @UseGuards(AuthGuard('local'))
  @Post('login')
  public async loginAsync(@Response() res, @Body() login: LoginModel) {
    const user = await this.usersService.getUserByUsernameAsync(login.username);
    if (!user) {
      res.status(HttpStatus.NOT_FOUND).json({
        message: 'User Not Found',
    } else {
      const token = this.authenticationService.createToken(user);
      return res.status(HttpStatus.OK).json(token);

In Postman, I am able to use my login endpoint to successfully login with the proper credentials and receive a JWT token. Then, I add an Authentication header to a GET request, copy and paste in the JWT token, and I have tried both "Bearer" and "JWT" schemes and both return 401 Unauthorized as you can see in the images below.

I used the JWT.IO debugger, to check if there's anything wrong with my token and it appears correct:

I am at a lost as to what could be the issue here. Any help would be greatly appreciated.

The problem may be in your request from Postman. Try to create new request and be cautious what you place in headers. If you are using bearer token place it in auth section, not in headers. Or place it in headers, not in auth section. Make a few experiments, it can help. – James Kent Jan 31, 2021 at 9:49

Note that the validate() function in your JWT strategy is only called after successful validation of the JWT. If you are consistently getting a 401 response when trying to use the JWT then you can't expect this function to be called.

The return from the validate() method is injected into the request object of any operation that's guarded with JWT authentication.

I'm not sure about the done() function that you're calling, but here's a working validate() method from a current project of mine:

async validate(payload: JwtPayload): Promise<User> {
  const { email } = payload
  const user = await this.authService.getActiveUser(email)
  if (!user) {
    throw new UnauthorizedException()
  return user

It looks like you're on the right track in the desire to return a user. Be sure that's what authenticationService.validateUserToken() actually does.

In the strategy, jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() seems correct, and in Postman using Authorization header with Bearer TOKEN also looks correct.

Regarding your authentication.controller.ts file, be careful about using @Request and @Response objects directly within your controllers in NestJS. These access the underlying framework e.g. Express and are liable to bypass many of the features implemented by Nest. Refer to https://docs.nestjs.com/faq/request-lifecycle to see what you're skipping out on...

You can return objects and throw errors directly from a decorated controller method (e.g. @Get(), Post(), etc) in NestJS and the framework will take care of the rest: HTTP code, JSON, etc.

From your controller consider ditching the @Reponse res and using throw new UnauthorizedException('User Not Found') and a simple return { token } (or similar) approach instead.

In your protected route I have found that explicitly declaring AuthGuard('jwt') works better and doesn't produce warnings in certain cases, even if you did set your default strategy to be JWT.

Do you actually need the AuthGuard('local') on your login route?

Inside your loginAsync() method DO NOT forget the crucial step of actually signing your token with your payload. You didn't provide your code for the createToken() method implementation in your authentication service, but I suspect that this may be what you're missing.

Consider this working implementation of a login service (which is simply called by it's controller's login function):

  async login(authCredentialsDto: AuthCredentialsDto): Promise<{ accessToken: string }> {
    const { email, password } = authCredentialsDto
    const success = await this.usersRepository.verifyCredentials(email, password)
    if (!success) {
      throw new UnauthorizedException('Invalid credentials')
    // roles, email, etc can be added to the payload - but don't add sensitive info!
    const payload: JwtPayload = { email } 
    const accessToken = this.jwtService.sign(payload)
    this.logger.debug(`Generated JWT token with payload ${JSON.stringify(payload)}`)
    return { accessToken }

Note that the jwtService is injected into the class via Dependency Injection by adding private jwtService: JwtService to the constructor params.

Also note in the above how an interface is defined for the JwtPayload so it is explicitly typed. This is better than using any as you are in your code.

Finally, if your JWT still doesn't validate, make absolutely sure that you are correctly using your token in Postman. Be extremely careful that you're not adding leading/trailing spaces, newlines, etc. I have made this mistake myself. You may want to sanity check by writing a quick JS file to try your API and make a fetch request that sets the Authorization header with value Bearer ${token}.

I hope this helps, good luck!

Thank you. This was a great help. What was happening is that I was using a package called jsonwebtoken and doing something like import * as jwt from 'jsonwebtoken' followed by jwt.sign(...). When using the actual JwtService from @nestjs/jwt, that fixed it. – noblerare Jul 9, 2020 at 15:38 I'm glad this helped, for sure using the actual @nestjs/jwt is the way to go with this approach! Cheers! – firxworx Jul 9, 2020 at 17:17 I have exactly same problem! Related to .env not being loaded correctly. It's the 3rd problem I've noticed related to this and still can't figure it out. – Pramus Nov 6, 2020 at 20:54

I had the same issue.

The issue in my case that the validate endpoint params where email and password,
while the nest auth documentation states that they should be username and password as the following:

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    return user;

Also be aware to send username and password in the body of the request.

Credits: https://github.com/nestjs/docs.nestjs.com/issues/875#issuecomment-619472086

You can pass options object to modify the property names. Have a look at this answer stackoverflow.com/a/71001043/7039250 – Gambitier Feb 6, 2022 at 3:38

Mine was that I was using RS256 algorithm to sign the JWT and I had a "Invalid algorithm" error.

So I added the "RS256" to my jwtStrategy constructor so now it look like this:

constructor(private configService: ConfigService) {
  super({
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
    ignoreExpiration: false,
    algorithms:["RS256"],
    secretOrKey: configService.get('jwtPublicKey'),

Then it gave me an error complaining about "no start line" on my public key file, the error was that I had an ssh-rsa key format instead of rsa-pem format, I solved with Get PEM file from ssh-rsa key pair

And the finally it worked.

I got all this info, putting a logger between the strategy output and the guard output, doing this JWT Auth Guard example

I had the same problem. Copied the code from the official documentation with '60s' :) Thanks man! – RobbeR Mar 3 at 11:45
Make Sure that inside your strategy the path of secretKey in 
secretOrKey is implemented correctly.
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: appConfig().appSecret,
    async validate(payload: any) {
    return { userId: payload.userId, username: payload.username };

I had a similar 401 status. My problem was that the token expiration was set to '240' I thought wrong it is 240 seconds... but not it was 240 miliseconds... after i change to '240s' it works fine../ read more about the 'zeit/ms' format here

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.