NestJS 8 own AuthGuard & Passport: Cannot read property 'authenticate' of undefined - passport.js

I have own implementation of AuthGuard:
import { authenticate } from 'passport';
#Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext){
...
authenticate(...);
}
}
When I call authenticate method I get: Cannot read property 'authenticate' of undefined at Authenticator.authenticate (.../node_modules/passport/lib/authenticator.js:162:26
It looks like Passport hasn't been started.
AuthModule:
#Module({
imports: [PassportModule],
providers: [
AuthGuard,
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AuthModule {}
and AppModule:
#Module({
imports: [
AuthModule
]
})
export class AppModule {}
This code was working in Nest 7.
Do you have any idea what could be wrong?

Related

Nest js convert module from forRoot to forRootAsync

#Module({
imports: [],
providers: [SupertokensService, AuthService],
exports: [],
controllers: [AuthController],
})
export class AuthModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
static forRoot({
connectionURI,
apiKey,
appInfo,
}: AuthModuleConfig): DynamicModule {
return {
providers: [
{
useValue: {
appInfo,
connectionURI,
apiKey,
},
provide: ConfigInjectionToken,
},
],
exports: [],
imports: [],
module: AuthModule,
};
}
}
The problem with this implementaion I can't use env variables, so I need useFactory to pass ConfigService. Can somebody do that, and give some explanation.
I figure out how to make this work, unfortunately it only works with nest.js version 9 (current latest version). First you need to create a new file. For example auth.module-definition.ts. Now in this file we need to create ConfigurableModuleBuilder.
import { ConfigurableModuleBuilder } from '#nestjs/common';
import { AuthModuleConfig } from './config.interface';
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AuthModuleConfig>()
.setClassMethodName('forRoot')
.build();
we need to set setClassMethodName('forRoot'), when we set forRoot it will create two methods, forRoot and forRootAsync. The next step is to extend our created ConfigurableModuleClass. it should look something like this
import { MiddlewareConsumer, Module } from '#nestjs/common';
import { AuthMiddleware } from './auth.middleware';
import { SupertokensService } from './supertokens/supertokens.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { ConfigurableModuleClass } from './auth.module-definition';;
#Module({
imports: [],
providers: [SupertokensService, AuthService],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule extends ConfigurableModuleClass {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
}
And that's actually it, so from now on we have forRootAsync and we get reigister it in app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '#nestjs/common';
import { AuthModule } from './auth/auth.module';
import { ConfigModule, ConfigType } from '#nestjs/config';
import authConfig from './auth/auth.config';
#Module({
imports: [
AuthModule.forRootAsync({
inject: [authConfig.KEY],
imports: [ConfigModule.forFeature(authConfig)],
useFactory: (config: ConfigType<typeof authConfig>) => {
return {
connectionURI: config.CONNECTION_URI,
appInfo: {
appName: config.appInfo.APP_NAME,
apiDomain: config.appInfo.API_DOMAIN,
websiteDomain: config.appInfo.WEBSITE_DOMAIN,
apiBasePath: config.appInfo.API_BASE_PATH,
websiteBasePath: config.appInfo.WEBSITE_BASE_PATH,
},
};
},
})
],
controllers: [],
providers: [
],
})
export class AppModule implements NestModule {
}
here I am using Nest.js Config, but you don't need to, so use it how you want.
I know that my english is not the best, so if you still do not understand you can check these sources https://docs.nestjs.com/fundamentals/dynamic-modules#configurable-module-builder
https://trilon.io/blog/nestjs-9-is-now-available#Configurable-module-builder

How to override provider for tests in NestJs and not create transitive dependencies that are not needed?

I am writing end-to-end tests in NestJs and using the "overrideProvider" function to setup test versions of various services (e.g. services that require database connections). I noticed though that even when I do this, the original implementation that injects the real database is still instantiated.
Is there a way to tell Nest to not create transitive dependencies that are overridden?
For example, I have a test that starts like:
...
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [ServiceModule],
})
// Works if I uncomment these lines:
// .overrideProvider('Database')
// .useValue(new TestDatabase())
.overrideProvider('ServiceUsingDatabase')
.useValue(new TestService())
.compile();
...
Where the module setup is like:
import { Inject, Injectable, Module } from '#nestjs/common';
interface Database {}
#Injectable()
class ProductionDatabase implements Database {
constructor() {
throw('Cannot create a production database.');
}
}
#Injectable()
export class TestDatabase implements Database {
constructor() {
console.log('Creating the test database.');
}
}
#Module({
providers: [
{
provide: 'Database',
useClass: ProductionDatabase
}
],
exports: ['Database']
})
class DatabaseModule {}
interface Service {}
#Injectable()
class ProductionService implements Service {
constructor(#Inject('Database') private readonly database: Database) {}
}
#Injectable()
export class TestService implements Service {
// Test implementation of the service does not Inject anything.
}
#Module({
imports: [DatabaseModule],
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: ProductionService
}
],
})
export class ServiceModule {}
But, the DI system is still seeming to try and instantiate ProductionDatabase. If I explicitly override the provider for the 'Database' it works, but I'd like to avoid having to explicitly list all transitive dependencies as such.
I ended up deciding to make a "Test" Module for every Module e.g.:
import { Inject, Injectable, Module } from '#nestjs/common';
interface Database {}
#Injectable()
class ProductionDatabase implements Database {
}
#Injectable()
export class TestDatabase implements Database {
}
#Module({
providers: [
{
provide: 'Database',
useClass: ProductionDatabase
}
],
exports: ['Database']
})
class DatabaseModule {}
#Module({
providers: [
{
provide: 'Database',
useClass: TestDatabase
}
],
exports: ['Database']
})
class TestDatabaseModule {}
interface Service {}
#Injectable()
class ProductionService implements Service {
constructor(#Inject('Database') private readonly database: Database) {}
}
#Injectable()
export class TestService implements Service {
}
#Module({
imports: [DatabaseModule],
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: ProductionService
}
],
})
export class ServiceModule {}
#Module({
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: TestService
}
],
})
export class TestServiceModule {}
etc... Though it turned out after some refactorings that the "Test" module wasn't needed as some Modules became pure business logic.

NestJs Authentication Circular Dependency

I am fairly new to nestJs. I am trying to implement an authentication process according to nestjs documentation, but I don't want to route the login in the app.controller I want to put it in the users.controller, so the route will be /users/login. However, since the AuthModule already uses the UsersService in my code and the login logic with JWT is in the AuthService. Importing it back to the UsersService create a circular dependency. I need help.
To avoid circular dependancy, in your UsersModule, you need to import your AuthModule with ForwardRef like that :
import ...
#Module( {
imports: [
forwardRef( () => AuthModule ),
],
controllers: [],
providers: [],
exports: []
} )
export class UsersModule {
}
If you haven't already done so, you'll need to export the AuthService in AuthModule :
import ...
#Module( {
imports: [],
controllers: [],
providers: [],
exports: [
AuthService
]
} )
export class AuthModule {
}

Nest can't resolve dependencies of the JwtService though exported from another module

I am using #nestjs/jwt in my NestJS project.
I have two modules, AuthModule and AppModule.
The AuthModule uses the #nestjs/jwt
The AppModule invoke auth service from AuthModule.
AuthService:
import { Injectable } from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { ForbiddenException } from '#nestjs/common';
#Injectable()
export class AuthService {
constructor(private readonly jwt: JwtService) {}
async validate(token: string) {
return this.jwt.verify(token);
}
...
}
AuthModule:
import { Module } from '#nestjs/common'
import { TokenService } from './auth.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { JwtModule } from '#nestjs/jwt'
#Module({
imports: [
JwtModule.register({
secret: "mysecret",
signOptions: { expiresIn: "60s" },
}),
],
// I provide AuthService & JwtService
providers: [AuthService, JwtService],
// I export AuthService and JwtService
exports: [AuthService, JwtService],
})
export class AuthModule {}
AppModule:
#Module({
imports: [
AuthModule,
...
],
controllers: [AppController],
// I provide AuthService & JwtService also in AppModule
providers: [AppService, JwtService],
})
export class AppModule {}
(The AppController invokes AuthService instance to validate token.)
I constantly get error:
Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0]
Why is that? Where do I miss?
You don't need to add JwtService to the providers. Nest will look through the current module's providers, then the imported module's exports, and then the global module's exports to find providers that are not immediately in the level it's resolving the providers in. This means that for AuthService it'll look up to the JwtModule's exports and find the JwtService already created, and use that instance. Then in the AppModule you don't need to mention the AuthService or JwtService, just import the AuthModule that exports the AuthService and JwtModule (doing some module re-exporting) and you should be good to go
EDIT: Added module code:
AuthModule
import { Module } from '#nestjs/common'
import { TokenService } from './auth.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { JwtModule } from '#nestjs/jwt'
#Module({
imports: [
JwtModule.register({
secret: "mysecret",
signOptions: { expiresIn: "60s" },
}),
],
providers: [AuthService],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
AppModule
#Module({
imports: [
AuthModule,
...
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

NestJS can't resolve a service dependency

I am trying to inject a ContentsService into a SlackModule so that I can use some of the Content functions in my Slack Controller.
This is app.module
#Module({
imports: [
ContentsModule,
ZapierModule,
SlackModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Here is my Contents Module:
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { ContentsController, ZapierController, SlackInteractiveController } from './contents.controller';
import { ContentsService } from './contents.service';
import { ContentSchema } from './content.model';
#Module({
imports: [
MongooseModule.forFeature([{ name: 'Content', schema: ContentSchema }]),
],
controllers: [ContentsController, ZapierController, SlackInteractiveController],
providers: [ContentsService],
exports: [ContentsService],
})
export class ContentsModule {}
This is my Slack Module:
import { Module } from '#nestjs/common';
import { SlackService } from './slack.service';
import { SlackController } from './slack.controller';
import { ContentsService } from '../contents/contents.service';
#Module({
providers: [SlackService],
controllers: [SlackController],
imports: [ContentsService],
})
export class SlackModule {}
And my Slack Controller
import { ContentsService } from '../contents/contents.service'
#Controller('slackevents')
export class SlackController {
constructor(private contentsService: ContentsService) {}
But when I run this code, I get this error:
[Nest] 75628 - 05/22/2020, 7:08 AM [ExceptionHandler] Nest can't resolve dependencies of the ContentsService (?). Please make sure that the argument at index [0] is available in the ContentsService context. +44ms\
What is it that I am doing wrong?
Services and other providers do not belong in the imports array. Only module classes should go in there. You should have imports: [ContentsModule] in your SlackModule and you'll have access to the ContentsService

Resources