Nestjs Authentication Project: "statusCode": 401, "message": "Unauthorized"

Viewed 202

I am learning NestJS. I tried to build a basic authentication project using NestJS and passport-local. But I got the error of "statusCode": 401,"message": "Unauthorized".

Here is the app module file:

import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { AppController } from './app.controller';
import { AuthModule } from './auth/auth.module';
import { UserService } from './users/users.services';
    
@Module({
   imports: [UsersModule, AuthModule],
   controllers: [AppController],
   providers: [UserService]
})
export class AppModule { }

Here is the app controller file:

import { Controller, Get, Post, Request, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
    
@Controller("app")
export class AppController {
  constructor() { }
   
  @Post()
  @UseGuards(AuthGuard("local"))
  async login(@Request() req) {
   // console.log(req.user);-----undefined
    return "This is private msg";
  }
}

Now here is the user data file:

export class User {
    name: string;
    password: string;
    age: number;
}

Now here is the user service file:

import { Injectable } from "@nestjs/common";
import { User } from "./users.data";
@Injectable()
export class UserService {
    public users: User[] = [
        {
            "name": "user1",
            "password": "admin",
            "age": 10
        },
        {
            "name": "user2",
            "password": "admin",
            "age": 102
        },
        {
            "name": "user3",
            "password": "admin",
            "age": 103
        },
        {
            "name": "user4",
            "password": "admin",
            "age": 104
        },
    ];
    
    async getUserByName(userName: string): Promise<User | undefined> {
        return this.users.find(user => user.name === userName);
    }
}

Here is the user module file:

import { Module } from '@nestjs/common';
import { UserService } from './users.services';
   
@Module({
    imports: [],
    controllers: [],
    providers: [UserService],
    exports: [UserService]
})
    
export class UsersModule { }

here is the Auth passport local file:

import { Injectable, Request, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-local";
import { UserService } from '../users/users.services';
import { User } from '../users/users.data';
    
@Injectable()
export class PassportlocalStragey extends PassportStrategy(Strategy) {
    constructor(private userService: UserService) {
        super();
    }
    
    
        async validate(username: string, password: string): Promise<any> {
        console.log('Here I go');
        const user = await this.userService.getUserByName(username);
        console.log('Got user', user);

        if (user == undefined) throw new UnauthorizedException();

        if (user && user.password === password) {
            return user;
        } else {
            console.log('Password mismatch');
            throw new UnauthorizedException();
        }
    }
}

Here is Auth module file:

import { Module } from '@nestjs/common';
import { PassportlocalStragey } from './passportlocal.auth';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { UserService } from 'src/users/users.services';
    
@Module({
    imports: [UsersModule, PassportModule],
    controllers: [],
    providers: [PassportlocalStragey],
    exports: []
})
    
export class AuthModule { }

Here is the output of the error:

{
  "statusCode": 401,
  "message": "Unauthorized"
}

enter image description here

Where is the problem? How can I solve this? Thank You.

2 Answers

In the PassportlocalStragey the validation method should be

async validate(username: string, password: string)

not validateUser.

EDIT: I have reproduced your code locally as it was bugging me. It works for me, here are my classes that might be slightly different from yours:

AuthModule

@Module({
  imports: [PassportModule],
  providers: [UserService, PassportLocalStrategy],
  exports: [UserService],
})
export class AuthModule {}

PassportLocalStrategy

@Injectable()
export class PassportLocalStrategy extends PassportStrategy(Strategy) {
  constructor(private userService: UserService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    console.log('Here I go');
    const user = await this.userService.getUserByName(username);
    console.log('Got user', user);

    if (user == undefined) throw new UnauthorizedException();

    if (user && user.password === password) {
      return user;
    } else {
      console.log('Password mismatch');
      throw new UnauthorizedException();
    }
  }
}

@Injectable()
export class LocalGuard extends AuthGuard('local') {}

AppController

@Controller('api')
export class AppController {
  constructor() {}

  @Post('login')
  @UseGuards(AuthGuard('local'))
  // @UseGuards(LocalGuard)
  login(@Req() req): any {
    return req.user;
  }
}

I did add LocalGuard class to the PassportLocalStrategy file. The guard on the route works for both @UseGuards(AuthGuard('local')) or @UseGuards(LocalGuard).

AppModule

@Module({
  imports: [AuthModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Passport expects you to send username and password as properties of req.body. You are sending name and password. If you want to use name instead of username you need to pass usernameField: 'name' to the super() call in your PassportlocalStragey #constructor

Related