nestjsbasic-authenticationpassport-local

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


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.


Solution

  • 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 {}