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"
}
Where is the problem? How can I solve this? Thank You.
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 {}