These are the partial NestJS code snippets I have. I am trying to implement the passport local strategy for getting the username and password. I am getting -Error: Unknown authentication strategy "local", in the controller file when using the auth guard.
AuthModule.ts
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { PassportModule } from '#nestjs/passport';
import { UserModule } from 'src/user/user.module';
import { JwtAuthController } from './jwt-auth.controller';
import { JwtAuthService } from './jwt-auth.service';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';
#Module({
imports: [
UserModule,
PassportModule,
JwtModule.register({
secret: process.env.SECRETKEY,
signOptions: { expiresIn: '3600s' }
})
],
controllers: [JwtAuthController],
providers: [JwtAuthService, LocalStrategy, JwtStrategy],
exports: [JwtAuthService],
})
export class JwtAuthModule {}
local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { JwtAuthService } from './jwt-auth.service';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
constructor(private authService: JwtAuthService) {
super();
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser({username, password});
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
app.controller.ts
import { Body, Controller, Get, Post, Req, UnauthorizedException, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { JwtAuthService } from './jwt-auth/jwt-auth.service';
#Controller()
export class AppController {
constructor(private readonly authService: JwtAuthService) {}
#UseGuards(AuthGuard('local'))
#Post('/auth/login')
async login(#Req() req) {
return this.authService.login(req.user)
}
}
I am getting the following error on calling /auth/login API
[Nest] 26753 - 10/31/2021, 22:08:18 ERROR [ExceptionsHandler] Unknown authentication strategy "local"
Error: Unknown authentication strategy "local"
Am I missing anything? Thanks in advance.
just add the PassportModule and LocalStrategy in app.module .it will be fixed
#Module({
imports: [PassportModule],
providers: [AuthService,LocalStrategy],
})
export class AppModule {}
Fix Bug https://github.com/nestjs/nest/issues/4646
File AUTH.ts
enter code here
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { PassportModule } from '#nestjs/passport';
import { UserModule } from 'src/user/user.module';
import { JwtAuthController } from './jwt-auth.controller';
import { JwtAuthService } from './jwt-auth.service';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';
#Module({
imports: [
UserModule,
PassportModule.register({defaultStrategy:'local'}),
JwtModule.register({
secret: process.env.SECRETKEY,
signOptions: { expiresIn: '3600s' }
})
],
controllers: [JwtAuthController],
providers: [JwtAuthService, LocalStrategy, JwtStrategy],
exports: [JwtAuthService],
})
export class JwtAuthModule {}
Such an old question with no answers...
In my case I was having sort of an cyclic dependency issue. Or at least it was what it looked like. Cyclic deps can be either on nest side or even on node.js require side (on the second case we end up with empty imports).
Case 1:
#Injectable()
export class MyLocalStrategy extends PassportStrategy(PassportLocalStrategy) {
hello = 'hello'
constructor(private authService: AuthService) {
console.log('load local strategy')
}
}
#Module({
imports: [
CommonModule,
PassportModule,
JwtModule.registerAsync({
async useFactory(config: ConfigService) {
const jwtSecret = config.get('APP_KEY')
const expiresIn = config.get('AUTH_TOKEN_EXPIRED')
return {
secret: jwtSecret,
signOptions: {
expiresIn,
},
}
},
inject: [ConfigService],
}),
],
providers: [
RoleService,
JwtStrategy,
JwtService,
AuthService,
],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
const moduleRef = this.moduleRef
console.log('init auth module')
const local = moduleRef.get(MyLocalStrategy)
const auth = moduleRef.get(AuthService)
console.log('my modules', { local, auth }, local?.hello)
}
}
In this case the "load local strategy" never got logged. Yet "my modules" logged an empty localStrategy instance, without my "hello" property. Weird!
I hacked a fix by moving the class instantiation to a factory provider, and by requiring the dependencies with ModuleRef. The following excerpt worked fine.
#Module({
imports: [
CommonModule,
PassportModule,
JwtModule.registerAsync({
async useFactory(config: ConfigService) {
const jwtSecret = config.get('APP_KEY')
const expiresIn = config.get('AUTH_TOKEN_EXPIRED')
return {
secret: jwtSecret,
signOptions: {
expiresIn,
},
}
},
inject: [ConfigService],
}),
],
providers: [
RoleService,
JwtStrategy,
JwtService,
AuthService,
{
provide: MyLocalStrategy,
useFactory: (moduleRef: ModuleRef) => {
const auth = moduleRef.get(AuthService)
console.log('local strat factory', { auth })
return new MyLocalStrategy(auth)
},
inject: [ModuleRef],
},
],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
const moduleRef = this.moduleRef
console.log('init auth module')
const local = moduleRef.get(MyLocalStrategy)
const auth = moduleRef.get(AuthService)
console.log('local', { local, auth }, local?.hello)
}
}
I also tried factory + injecting AuthService directly (through providers array, instead of using ModuleRef). I got a null AuthService.
Probably some dependency cycle on the node.js module imports, tough neither eslint or nest wouldn't say anything.
Check to see if your auth module is being imported in your entry file. app.module.ts.
My auth-module.ts
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
#Module({
imports:[UsersModule,PassportModule],
providers: [AuthService,LocalStrategy]
})
export class AuthModule {}
My app module
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { UsersModule } from 'src/users/users.module';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
#Module({
imports:[UsersModule,PassportModule],
providers: [AuthService,LocalStrategy]
})
export class AuthModule {}
NestJs encourages scalability and therefore organizes your code in modules.
Instead of calling JwtAuthService method in the AppController, do it in the JwtAuthController. Your code should look like this:
import { Body, Controller, Get, Post, Req, UnauthorizedException, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { JwtAuthService } from './jwt-auth.service';
#Controller('auth')
export class JwtAuthController {
constructor(private readonly authService: JwtAuthService) {}
#Post('/login')
#UseGuards(AuthGuard('local'))
async login(#Req() req) {
return req.user
}
}
Nest will then instantiate all providers in the JwtAuthModule such that it will be available in it's controller (JwtController).
You Can always optimize your code by moving AuthGuard to it own file local.guard.ts given you the possibility to do any additional business logic.
import { ExecutionContext } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { Request } from 'express';
export class LocalGuard extends AuthGuard('local') {}
You need to import the module that implements the authentications.
It can be inside app.module.ts or inside another module that is connected to it.
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
#Module({
imports: [UsersModule, AuthModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
There is a workaround that may clarify things better
https://docs.nestjs.com/techniques/authentication#request-scoped-strategies
I have the same issue with you. My solution is importing LocalStrategy class to module file, in which I use AuthGuard for API endpoint.
For example: in file user.controller.ts I have AuthGuard('local') in the endpoint /login with method POST
//user.controller.ts
#UseGuards(AuthGuard('local'))
#Post('login')
async login (#Request() req: any) {
return req.user;
}
In my case, I just add LocalStrategy class (I also have this class in local.strategy.ts like you) in module file:
//user.module.ts
#Module({
imports: [
...
LocalStrategy,
],
controllers: [UserController],
...
}
Conclusion, NestJs is quite complicated in importing modules, don't forget to import what you want into other modules. Good luck!
Related
Issue with JWT service I'm trying to use for user auth
ERROR [ExceptionHandler] Nest can't resolve dependencies of the AuthService (?, JwtService). Please make sure that the argument UsersService at index [0] is available in the AuthModule context.
Here is the auth.module
import { Module } from "#nestjs/common"
import { JwtModule } from '#nestjs/jwt';
import { UsersModule } from "../users/users.module";
import { AuthService } from "./auth.service"
import { PassportModule } from "#nestjs/passport";
import { AuthController } from './auth.controller';
import { MongooseModule } from "#nestjs/mongoose"
import { UserSchema } from "users/schemas/users.schema";
import { LocalStrategy } from './local.auth';
#Module({
imports: [UsersModule, PassportModule, JwtModule.register({
secret: 'secretKey',
signOptions: { expiresIn: '60s' },
}), MongooseModule.forFeature([{ name: "Users", schema: UserSchema }])],
providers: [AuthService, LocalStrategy],
controllers: [AuthController],
})
export class AuthModule { }
Here is the App.module
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { MongooseModule } from '#nestjs/mongoose';
import { KpiModule } from './kpi/kpi.module';
import { AuthModule } from './auth/auth.module';
#Module({
imports: [
MongooseModule.forRoot('mongodb://127.0.0.1:27017/vstkpi?readPreference=primary'),
UsersModule,
KpiModule,
AuthModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
and userService
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from 'users/interfaces/users.interface';
#Injectable()
export class UsersService {
constructor(#InjectModel('Users') private readonly userModel: Model<User>) {}
//Get all users
async getUsers(): Promise<User[]> {
const users = await this.userModel.find().exec();
return users
}
//Get single user
async getUser(query: object ): Promise<User> {
return this.userModel.findOne(query);
}
async addUser(
firstname: string,
lastname: string,
jobtitle: string,
startdate: string,
password: string,
email: string): Promise<User> {
return this.userModel.create({
firstname,
lastname,
jobtitle,
startdate,
password,
email
});
}
}
I have tried to reorder imports and such but its not firing past that error
added userModule
import { Module } from '#nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { UserSchema } from 'users/schemas/users.schema';
import { MongooseModule } from '#nestjs/mongoose';
#Module({
imports: [
MongooseModule.forFeature([{ name: 'Users', schema: UserSchema }])
],
providers: [UsersService],
controllers: [UsersController]
})
export class UsersModule { }
you didn't exported UsersService from UsersModule module, so that provider is not visible to other modules.
Just add this to UsersModule config:
exports: [UsersService]
just when I thought I do understand how the modules work and starting to play with guards which leads me to this error in full
Error: Nest can't resolve dependencies of the CanModifyGuard (UsersService, ?). Please make sure that the argument TelevisionsService at index [1] is available in the UsersModule context.
Potential solutions:
- If TelevisionsService is a provider, is it part of the current UsersModule?
- If TelevisionsService is exported from a separate #Module, is that module imported within UsersModule?
#Module({
imports: [ /* the Module containing TelevisionsService */ ]
})
CanModifyGuard was fine when the guard is using only TelevisionsService, once I added the UsersService and add the guard into the UsersController
The error above popped up.
Wonder if I can have another pair of eyes where I am setting this wrong?
app.module.ts
import { MiddlewareConsumer, Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '#nestjs/mongoose';
import { ConfigModule } from '#nestjs/config';
import { UsersModule } from './resources/users/users.module';
import { AuthModule } from './resources/auth/auth.module';
import { CommonModule } from './resources/common/common.module';
import { TelevisionsModule } from './resources/televisions/televisions.module';
import { CurrentUserMiddleware } from './common/middlewares/current-user.middleware';
#Module({
imports: [
ConfigModule.forRoot(),
MongooseModule.forRoot(process.env.DATABASE_URL),
UsersModule,
AuthModule,
CommonModule,
TelevisionsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(CurrentUserMiddleware).forRoutes('*');
}
}
can-modify.guard.ts
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '#nestjs/common';
import { TelevisionsService } from '../../resources/televisions/televisions.service';
import { UsersService } from '../../resources/users/users.service';
#Injectable()
export class CanModifyGuard implements CanActivate {
constructor(
private readonly usersService: UsersService,
private readonly televisionsService: TelevisionsService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
//logics here
}
}
users.module.ts
import { Module } from '#nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { MongooseModule } from '#nestjs/mongoose';
import { User, UserSchema } from './user.entity';
#Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
exports: [UsersService],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
televisions.module.ts
import { Module } from '#nestjs/common';
import { TelevisionsController } from './televisions.controller';
import { TelevisionsService } from './televisions.service';
import { MongooseModule } from '#nestjs/mongoose';
import { Television, TelevisionSchema } from './television.entity';
import { UsersModule } from '../users/users.module';
#Module({
imports: [
MongooseModule.forFeature([
{ name: Television.name, schema: TelevisionSchema },
]),
UsersModule,
],
exports: [TelevisionsService],
controllers: [TelevisionsController],
providers: [TelevisionsService],
})
export class TelevisionsModule {}
auth.module.ts
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
import { ConfigModule } from '#nestjs/config';
import authConfig from './config/auth.config';
#Module({
imports: [UsersModule, ConfigModule.forFeature(authConfig)],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
common.module.ts (dont think this file would have any effect but just in case)
import { Module } from '#nestjs/common';
import { APP_GUARD } from '#nestjs/core';
import { AuthorizationRolesGuard } from '../../common/guards/authorization-roles.guard';
#Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthorizationRolesGuard,
},
],
})
export class CommonModule {}
In order to use the guard, I added the guard to the specific route #UseGuards(CanModifyGuard) and again, it is working fine when I added this guard into television controller but once I added it to user controller the error pops up.
As the error mentioned for potential solutions even though UsersModule is not using any of the TelevisionsService I still imported TelevisionsModule into UsersModule but no luck on getting this fix...and instead I would get another error and said potential cause A circular dependency between modules. Use forwardRef() to avoid it. I read the doc about it and also tried using forwardRef() but still didn't fix it. I might have put the forwardRef() in the wrong place because I am not sure where I should use it.
Thank you in advance for any suggestions or advices.
If I'm understanding your set up correctly, you have a circular dependency on the modules, so you need to forwardRef the imports for those. Using these modules should fix your issue:
user.module.ts
#Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
forwardRef(() => TelvisionsModule),
],
exports: [UsersService],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
televisons.module.ts
#Module({
imports: [
MongooseModule.forFeature([
{ name: Television.name, schema: TelevisionSchema },
]),
forwardRef(() => UsersModule),
],
exports: [TelevisionsService],
controllers: [TelevisionsController],
providers: [TelevisionsService],
})
export class TelevisionsModule {}
Hi I have the following code in NestJS.I am using moduleRef and have declared a custom provider named 'blogService'.However I am getting an error that says:
'Nest can't resolve dependencies of the blogService (?). Please make
sure that the argument BlogModel at index [0] is available in the
AppModule context.'
.What exactly am I doing wrong while declaring the custom provider which is leading to this error as it seems that I am injecting the Mongoose Model as well?
app.module.ts
import { BlogService } from './blog/service/blog.service';
import { Blog } from './blog/schema/blog.schema';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '#nestjs/config';
import { MongooseModule, getModelToken } from '#nestjs/mongoose';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, load: [configuration], }),
MongooseModule.forRoot(process.env.DATABASE_URL)
],
controllers: [AppController],
providers: [AppService, {
provide: 'blogService',
useFactory: () => BlogService,
inject: [getModelToken(Blog.name)]
}],
})
app.controller.ts
import { Controller, Get } from '#nestjs/common';
import { ModuleRef } from '#nestjs/core';
import { BlogService } from './blog/service/blog.service';
#Controller()
export class AppController {
constructor(private readonly modelRef: ModuleRef) { }
#Get('blogs')
async getAllBlogs(): Promise<any> {
const response = await this.modelRef.get('blogService', { strict: false }).getAllBlogs();
return response;
}
}
blog.service.ts
import { Injectable, HttpException, HttpStatus, Logger } from '#nestjs/common';
import { Blog } from '../schema/blog.schema';
import { InjectModel } from '#nestjs/mongoose';
import { Model, Types } from 'mongoose';
#Injectable()
export class BlogService {
private readonly logger = new Logger(BlogService.name);
constructor(#InjectModel(Blog.name) private blogModel: Model<Blog>) { }
async getAllBlogs() {
try {
const blogs = await this.blogModel.find().exec();
return blogs;
} catch (error) {
this.logger.error(error.message);
}
}
}
Not really sure why you're making a custom provider for this, other than possibly academic purposes. Either way, for anything you want to #InjectModel() or use getModelToken() for, you needx to have a MongooseModule.forFeature() call to register the custom provider the #nestjs/mongoose package will create for you. Once you have this you can use #InjectModel() or a custom provider like
{
provide: 'blogService',
inject: [getModelToken(Blog.name)],
useFactory: (model: Model<Blog>) => new BlogService(model)
}
I've just started with NestJS and I'm wondering what the best way to solve this error is
Nest cannot export a provider/module that is not a part of the
currently processed module (UsersModule). Please verify whether the
exported UsersService is available in this particular context.
I am following along with the documentation here and I seem to have hit a brick wall. My app won't compile so I can't test any further. I'll add some of the files as examples.
auth.module.ts
import { Module } from '#nestjs/common';
import { UsersModule } from 'src/users/users.module';
import { AuthController } from './controllers/auth/auth.controller';
import { AuthService } from './services/auth/auth.service';
#Module({
imports: [UsersModule],
controllers: [AuthController],
providers: [AuthService],
exports: [AuthService],
})
export class AuthModule {}
users.module.ts
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { UsersController } from './controllers/users/users.controller';
import { User, UserSchema } from './schemas/user.schema';
import { UsersService } from './services/users/users.service';
#Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
controllers: [UsersController],
providers: [
{
provide: 'USERS_SERVICE',
useClass: UsersService,
},
],
exports: [UsersService],
})
export class UsersModule {}
users.service.ts
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateUserDto } from 'src/users/dto/user.dto';
import { User, UserDocument } from 'src/users/schemas/user.schema';
import { IUserService } from './user';
#Injectable()
export class UsersService implements IUserService {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
// async code for database modification
}
}
Any help would be greatly appreciated! Many thanks!
The token of UsersService provider is the string 'UsersService' (or UsersService.name) not 'USERS_SERVICE'
So just use the short-hand syntax providers: [UsersService] or the right token in provide:.
I'm working in a new Nest.js project and whenever I try to start the server:
[ExceptionHandler] Nest can't resolve dependencies of the RestController (?). Please make sure that the argument dependency at index [0] is available in the RestModule context.
I've checked the documentation and looked everywhere and i cannot spot the problem. The RestController class injects UsersService, which is exported by UsersModule and imported in RestModule. Everything looks fine but didn't work.
This is my code:
app.module.ts:
import { Module } from '#nestjs/common'
import { RestModule } from './rest/rest.module'
#Module({
imports: [
RestModule
]
})
export class AppModule { }
rest.module.ts:
import { Module } from '#nestjs/common'
import { RestController } from './rest.controller'
import { UsersModule } from '../users/users.module'
#Module({
imports: [
UsersModule
],
controllers: [
RestController
]
})
export class RestModule { }
rest.controller.ts:
import { Controller, Inject, Get } from '#nestjs/common'
import { UsersService } from '../users/users.service'
import { User } from '../users/user.interface'
#Controller()
export class RestController {
constructor (
#Inject()
private readonly usersService: UsersService
) { }
#Get()
async getAll (): Promise<User[]> {
return this.usersService.findAll()
}
}
users.module.ts:
import { Module } from '#nestjs/common'
import { UsersService } from './users.service'
#Module({
providers: [
UsersService
],
exports: [
UsersService
]
})
export class UsersModule { }
users.service.ts:
import { Injectable } from '#nestjs/common'
import { User } from './user.interface'
#Injectable()
export class UsersService {
async findAll (): Promise<User[]> {
return new Promise((resolve, reject) => resolve([]))
}
}
Thank you in advance!
In your RestController class you have to remove the #Inject() from your constructor:
constructor (
private readonly usersService: UsersService
) { }
#Inject() can be used for non-class-based provider tokens.