I have created an auth middlewere for checking each request, the middlewere is using server (only if data was not found in the req.connection).
I'm trying to inject the service into my middlewere and I keep getting the same error "Nest can't resolve dependencies of the AuthenticationMiddleware (?). Please verify whether [0] argument is available in the current context."
AuthenticationModule:
#Module({
imports: [ServerModule],
controllers: [AuthenticationMiddleware],
})
export class AuthenticationModule {
}
AuthenticationMiddleware:
#Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
constructor(private readonly service : UserService) {}
resolve(): (req, res, next) => void {
return (req, res, next) => {
if (req.connection.user)
next();
this.service.getUsersPermissions()
}
}
ServerModule:
#Module({
components: [ServerService],
controllers: [ServerController],
exports: [ServerService]
})
export class ServerModule {}
ApplicationModule:
#Module({
imports: [
CompanyModule,
ServerModule,
AuthenticationModule
]
})
export class ApplicationModule implements NestModule{
configure(consumer: MiddlewaresConsumer): void {
consumer.apply(AuthenticationMiddleware).forRoutes(
{ path: '/**', method: RequestMethod.ALL }
);
}
}
Your application can't resolve AuthMiddleware dependencies probably because you inject an UserService into it but the ServerModule that you import into your AuthenticationModulejust exports a ServerService. So, what should be made is:
#Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
constructor(private readonly service : ServerService) {}
resolve(): (req, res, next) => void {
return (req, res, next) => {
if (req.connection.user)
next();
this.service.getUsersPermissions()
}
}
You can find more about the NestJS dependency container here.
Related
I'm trying to get PrismaService on my main.ts, but it's keep crashing. I'm new on this, can anyone help me to solve it?
My prisma.service.ts:
import { INestApplication, Injectable, OnModuleInit } from '#nestjs/common';
import { PrismaClient } from '#prisma/client';
#Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
My main.ts:
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './prisma.service';
import { ValidationPipe } from '#nestjs/common';
import helmet from 'helmet';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
allowedHeaders: '*',
origin: '*',
});
app.use(helmet());
app.use(helmet.hidePoweredBy());
app.use(helmet.contentSecurityPolicy());
const prismaService = app.get(PrismaService);
await prismaService.enableShutdownHooks(app);
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);
await app.listen(process.env.PORT, () => console.log('runing...'));
}
bootstrap();
The error message:
Error: Nest could not find PrismaService element (this provider does not exist in the current context)
at InstanceLinksHost.get (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/injector/instance-links-host.js:15:19)
at NestApplication.find (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/injector/abstract-instance-resolver.js:8:60)
at NestApplication.get (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-application-context.js:64:20)
at /home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-factory.js:133:40
at Function.run (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/errors/exceptions-zone.js:10:13)
at Proxy.<anonymous> (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-factory.js:132:46)
at Proxy.<anonymous> (/home/rafittu/wophi/alma/back/node_modules/#nestjs/core/nest-factory.js:181:54)
at bootstrap (/home/rafittu/wophi/alma/back/src/main.ts:18:29)
When I delete PrismaService from main.ts, server start normaly
Use app.get(PrismaService, { strict: false }). The strict: false is to say that AppModule doesn't directly provider the provider and to traverse the DI container to find it
After setting up the PrismaService in prisma.service.ts and enabling shutdown hooks in the main.ts file, you also need to do the following:
In the app.module.ts you need to add PrismaService as one of the providers:
...
import { PrismaService } from './prisma/prisma.service';
#Module({
imports: [],
controllers: [AppController],
providers: [AppService, PrismaService], // add PrismaService here
})
export class AppModule {}
i want know how to inject ConfigService in PassportModule AuthGuard with custom params?
here is my code
auth.module.ts
#Module({
imports: [
PassportModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService): Promise<IAuthModuleOptions> => {
return {}
}
}),
],
// ...
})
export class AuthModule {}
github-auth.guard.ts
#Controller('auth')
export class AuthController {
// ...
#Get('github-oauth')
#UseGuards(new GithubAuthGuard({}))
async githubOAuth(#Req() req: Request, #Res() res: Response) {
const user = req.user
return await this.handleOauth(user, req, res)
}
}
github-auth.guard.ts
import { ConfigService } from '#nestjs/config'
import { AuthGuard } from '#nestjs/passport'
#Injectable()
export class GithubAuthGuard extends AuthGuard('github') {
constructor(private readonly configService: ConfigService) {
super()
}
//...
}
i call the UseGuards with new GithubAuthGuard({}) because i want pass custom params.
Update:
AuthGuard('github') return a wraped class can accept options the then pass down to my custom strategy's authenticate function as the second argument.
here is my github.strategy.ts
import { Strategy } from 'passport'
class StrategyFoo extends Strategy {
constructor(options, verify) {
//...
}
// options from AuthGuard('github')
authenticate(req, options) {
const self = this
const redirect_uri = options.callbackURL || this._options.callbackURL
// ...
}
}
#Injectable()
export class GithubBarStrategy extends PassportStrategy(StrategyFoo, 'github') {
//...
}
export const GithubStrategy = GithubBarStrategy
after some research i figure it out
Nest will not inject anything on a manually instantiated class
so just call #UseGuards(GithubAuthGuard) and then inject ConfigService in github-auth.guard.ts or github.strategy.ts like:
inject in github-auth.guard.ts
#Injectable()
export class GithubAuthGuard extends AuthGuard('github') {
// #Inject(ConfigService)
// private configService: ConfigService
constructor(private readonly configService: ConfigService) {
//
const options = {
callbackURL: configService.get('OAuth.github.callbackURL'),
//...
}
super(options)
}
getAuthenticateOptions(context) {
// this also works
return {
callbackURL: this.configService.get('OAuth.github.callbackURL'),
//...
}
}
// ...
}
or
inject in github.strategy.ts
class StrategyFoo extends Strategy {
private _options: any
constructor(options, verify) {
//...
this._options = options
// ...
}
authenticate(req, options) {
// ...
let _options = {
...options,
...this._options
}
// ...
}
}
#Injectable()
export class GithubBarStrategy extends PassportStrategy(StrategyFoo, 'github') {
constructor(private readonly configService: ConfigService) {
super({
passReqToCallback: true,
callbackURL: this.configService.get('OAuth.github.callbackURL'),
// ...
})
}
// ...
}
I need help with processing after authentication using Nest.js
here do I pass the failureRedirect option for passport-local when using Nest.js for authentication?
Without Nest.js
app.post('/login', passport.authenticate('local', {
//Passing options here.
successRedirect: '/',
failureRedirect: '/login'
}));
My code is. (with Nest.js)
local.strategy.ts
import { Injectable, UnauthorizedException } from "#nestjs/common";
import { PassportStrategy } from "#nestjs/passport";
import { Strategy } from "passport-local";
import { AuthService } from "./auth.service";
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
//I tried passing the option here. but failed.
})
}
async validate(username: string, password: string): Promise<string | null> {
const user = this.authService.validate(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
local.guard.ts
import { Injectable } from "#nestjs/common";
import { AuthGuard } from "#nestjs/passport";
#Injectable
export class LocalAuthGuard extends AuthGuard('local') {}
auth.controller.ts
import { Controller, Get, Post, Render, UseGuards } from "#nestjs/common";
import { LocalAuthGuard } from "./local.guard";
#Controller()
export class AuthController {
#Get("/login")
#Render("login")
getLogin() {}
//Redirect to '/login' when authentication failed.
#UseGuards(LocalAuthGuard)
#Post("/login")
postLogin() {}
}
auth.module.ts
import { Module } from "#nestjs/common";
import { PassportModule } from "#nestjs/passport";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
import { LocalStrategy } from "./local.strategy";
import { LocalAuthGuard } from "./local.guard";
#Module({
controllers: [AuthController],
imports: [PassportModule],
providers: [AuthService, LocalStrategy, LocalAuthGuard]
})
export class AuthModule {}
I tried adding code to AuthController#postLogin to redirect on login failure, but the code seems to run only on successful login.
I would like to redirect to the login page again in case of login failure with the failureRedirect option of passport-local.
I found a workaround since using the passport options sadly didn't work:
#Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
getAuthenticateOptions(context: ExecutionContext): IAuthModuleOptions {
return {
successReturnToOrRedirect: '/',
failureRedirect: '/login',
};
}
}
Instead I created a Nestjs Filter to catch an exception containing a redirect URL.
redirecting.exception.ts
export class RedirectingException {
constructor(public url: string) {}
}
redirecting-exception.filter.ts
import { ArgumentsHost, Catch, ExceptionFilter } from '#nestjs/common';
import { Response } from 'express';
import { RedirectingException } from './redirecting.exception';
#Catch(RedirectingException)
export class RedirectingExceptionFilter implements ExceptionFilter {
catch(exception: RedirectingException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
response.redirect(exception.url);
}
}
In my validate method I'm throwing the RedirectingException with the correct error msg, e.g.
throw new RedirectingException('/login?error="User not found"');
And the controller handles the rest of the redirecting and passes the error to the view, so it can be displayed:
#Get('/login')
#Render('login.pug')
#Public()
async login(#Query() query) {
return { error: query.error };
}
#Post('/login')
#Public()
#UseGuards(LocalAuthGuard)
#Redirect('/')
async doLogin() {}
I'd rather use the passport functionality including the failureFlash, but I couldn't get it to work.
I'm trying here to implement a token-based authentication using the passport-headerapikey library.
This is what I've tried so far, and for some reason I have a 500 server error popping from somewhere I couldn't find.
This is the structure of my authentication system (I also have a JWT-token based strategy in parallel on my graphQL queries).
app.module
#Module({
imports: [
AuthModule,
],
controllers: [AppController],
providers: [
AppService
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('/datasource/:id');
}
}
auth.module
#Module({
imports: [
PassportModule,
],
providers: [
AuthService,
DatasourceTokenStrategy,
],
controllers: [],
exports: [AuthService],
})
export class AuthModule {}
datasourceToken.strategy
#Injectable()
export class DatasourceTokenStrategy extends PassportStrategy(
HeaderAPIKeyStrategy,
'datasourceToken',
) {
constructor(private authService: AuthService) {
super(
{ header: 'datasourceToken', prefix: '' },
true,
(apikey, done, req) => {
const checkKey = authService.validateDatasourceToken(apikey);
if (!checkKey) {
return done(false);
}
return done(true);
},
);
}
}
authMiddleware.strategy
import {
Injectable,
NestMiddleware,
UnauthorizedException,
} from '#nestjs/common';
import * as passport from 'passport';
#Injectable()
export class AuthMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
passport.authenticate(
'datasourceToken',
{ session: false, failureRedirect: '/api/unauthorized' },
(value) => {
if (value) {
next();
} else {
throw new UnauthorizedException();
}
},
)(req, res, next);
}
}
This is the error thrown when testing the endpoint with Jest:
When running my debug mode, I can see that the datasourceToken strategy is ok (I can retrieve the datasourceToken properly and validate it), but I think the problem is happening after my auth middleware..
Thanks guys for your insights
The function "done()" takes 3 arguments.
done(error, user, info)
You need to pass null as the first argument to let passport know that there was no error while authenticating.
done(null, true)
I've a AuthGuard who check the JWT token in controllers. I want use this Guard in controllers to check authentication. I've this error:
Nest can't resolve dependencies of the AuthGuard (?, +). Please make sure that the argument at index [0] is available in the current context.
TestController.ts
import {
Controller,
Post,
Body,
HttpCode,
HttpStatus,
UseInterceptors,
UseGuards,
} from "#nestjs/common";
import { TestService } from "Services/TestService";
import { CreateTestDto } from "Dtos/CreateTestDto";
import { ApiConsumes, ApiProduces } from "#nestjs/swagger";
import { AuthGuard } from "Guards/AuthGuard";
#Controller("/tests")
#UseGuards(AuthGuard)
export class TestController {
constructor(
private readonly testService: TestService,
) {}
#Post("/create")
#HttpCode(HttpStatus.OK)
#ApiConsumes("application/json")
#ApiProduces("application/json")
async create(#Body() createTestDto: CreateTestDto): Promise<void> {
// this.testService.blabla();
}
}
AuthGuard.ts
import { CanActivate, ExecutionContext, Injectable } from "#nestjs/common";
import { AuthService } from "Services/AuthService";
import { UserService } from "Services/UserService";
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private readonly authService: AuthService,
private readonly userService: UserService,
) {}
async canActivate(dataOrRequest, context: ExecutionContext): Promise<boolean> {
try {
// code is here
return true;
} catch (e) {
return false;
}
}
}
AuthService (the dependency that could not be resolved) must be available in the scope containing the controller which uses the guard.
What does it mean?
Include AuthService in the providers of the module loading your controller.
e.g.
#Module({
controllers: [TestController],
providers: [AuthService, TestService, UserService],
})
export class YourModule {}
EDIT - Forgot to mention that an other clean way (maybe cleaner, depending on the context) consists in importing the module that offers (exports) the service.
e.g.
#Module({
providers: [AuthService],
exports: [AuthService],
})
export class AuthModule {}
#Module({
imports: [AuthModule],
controllers: [TestController],
providers: [TestService, UserService],
})
export class YourModule {}