JwtStrategy requires a function to retrieve jwt from requests - Tried other posts already - nestjs

I have this weird issue with my code, I followed a tutorial and I've search forums for solutions, menitoning something about passport v1 and v2 versions. fromAuthHeaderWithScheme('jwt') in my code from beginnig, but it seems to not work.
rt.strategy.ts
import { PassportStrategy } from '#nestjs/passport/dist';
import { ExtractJwt } from 'passport-jwt';
import { Strategy } from 'passport-local';
import { Request } from 'express';
import { Injectable } from '#nestjs/common';
#Injectable()
export class RtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
constructor() {
super({
jwtFormRequest: ExtractJwt.fromAuthHeaderWithScheme('jwt'),
ignoreExpiration: false,
secretOrKey: 'rt-secret',
passReqToCallback: true,
});
}
validate(req: Request, payload: any) {
const refreshToken = req.get('authorization').replace('Bearer', '').trim();
return {
...payload,
refreshToken,
};
}
}
at.strategy.ts
import { Injectable } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport/dist';
import { ExtractJwt, Strategy } from 'passport-jwt';
#Injectable()
export class AtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor() {
super({
jwtFormRequest: ExtractJwt.fromAuthHeaderWithScheme('jwt'),
ignoreExpiration: false,
secretOrKey: 'at-secret',
});
}
validate(payload: any) {
return payload;
}
}
It's really weird and I don't know what else to do :(
[Nest] 25408 - 18/02/2023, 02:16:21 ERROR [ExceptionHandler] JwtStrategy requires a function to retrieve jwt from requests (see option jwtFromRequest)
TypeError: JwtStrategy requires a function to retrieve jwt from requests (see option jwtFromRequest)
at new JwtStrategy (D:\GitHub\gamvive_api\node_modules\passport-jwt\lib\strategy.js:55:15)
at new MixinStrategy (D:\GitHub\gamvive_api\node_modules\#nestjs\passport\dist\passport\passport.strategy.js:32:13)
at new AtStrategy (D:\GitHub\gamvive_api\src\auth\strategies\at.strategy.ts:8:5)
at Injector.instantiateClass (D:\GitHub\gamvive_api\node_modules\#nestjs\core\injector\injector.js:351:19)
at callback (D:\GitHub\gamvive_api\node_modules\#nestjs\core\injector\injector.js:56:45)
at Injector.resolveConstructorParams (D:\GitHub\gamvive_api\node_modules\#nestjs\core\injector\injector.js:136:24)
at Injector.loadInstance (D:\GitHub\gamvive_api\node_modules\#nestjs\core\injector\injector.js:61:13)
at Injector.loadProvider (D:\GitHub\gamvive_api\node_modules\#nestjs\core\injector\injector.js:88:9)
at D:\GitHub\gamvive_api\node_modules\#nestjs\core\injector\instance-loader.js:49:13
at async Promise.all (index 4)
I've searched for forums, they all menitone to add fromAuthHeaderWithScheme('jwt'), but I have it from the beginning

What version of passport-jwt are you using?
Can you go to your node_modules/passport-jwt/lib/extract_jwt.js and check the extractors.fromAuthHeaderWithScheme function?
It can be that you are using "wrong" #types/passport-jwt, meaning that the types are for the newer lib version and fromAuthHeaderWithScheme is in fact undfined

Related

User and its data is undefined on the context.switchToHttp().getRequest() with nestjs guards

I'm new to nestJs and I needed to add role based access to the application so I followed the documentation but in the execution context user doesn't exist. I can't seems to find the problem
here is the full repo https://github.com/callme-MJ/test-sample
roles.guard.ts
import { CanActivate, ExecutionContext, Injectable } from "#nestjs/common";
import { Reflector } from "#nestjs/core";
import { use } from "passport";
import { Role } from "src/typeorm/roles.enum";
#Injectable()
export class RolesGuard implements CanActivate{
constructor (private reflector:Reflector){}
canActivate(context: ExecutionContext): boolean {
const requiredRole = this.reflector.getAllAndOverride<Role[]>('roles',[
context.getHandler(),
context.getClass(),
]);
if (!requiredRole) {
return true
}
const {user} = context.switchToHttp().getRequest();
return requiredRole.some((role)=> user.role.includes(role));
}
}
auth.controller.ts
import { Controller, Get, Post, Request, UseGuards } from '#nestjs/common';
import { AuthService } from 'src/auth/services/auth/auth.service';
import { Role } from 'src/typeorm/roles.enum';
import { JwtAuthGuard } from 'src/utils/guards/jwt.auth.guard';
import { LocalAuthGuard } from 'src/utils/guards/local-auth.guards';
import { RolesGuard } from 'src/utils/guards/roles.guards';
import { Roles } from 'src/utils/roles.decorator';
#Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) { }
#UseGuards(JwtAuthGuard,RolesGuard)
#Get('dashboard')
#Roles(Role.CORDINATOR)
getResponse(#Request() req): any {
return req.user;
}
}
At this line in your repo you have APP_GUARD which is a global guard binding. This means that this RolesGuard will be registered and used before your JwtAuthGuard in the getResponse method of the AuthController. Take a look at the section on "Enabling Authentication Globally" for a way around this. Otherwise, you can remove the APP_GUARD for the RolesGuard and just bind it where necessar

How can I user configService in super()?

I have a question about setting environmental variables.
In official document, it says using ConfigModule in this case, but my case is a exception case.
Because I would like to use it in super() in constructor.
My code is the below.
Is there any solution in this case?
If you need more information, please let me know.
Thank you all your support!!
// jwt.strategy.ts
import { UnauthorizedException } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { PassportStrategy } from '#nestjs/passport';
import { InjectRepository } from '#nestjs/typeorm';
import { Strategy, ExtractJwt } from 'passport-jwt';
import { JwtPayload } from './jwt-payload.interface';
import { UserRepository } from './user.repository';
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
#InjectRepository(UserRepository)
private userRepository: UserRepository,
private configService: ConfigService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: configService.get('JWT_TOKEN'),
});
}
async validate(payload: JwtPayload) {
const { username } = payload;
const user = await this.userRepository.findOne({ username });
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
For your Strategy you're missing the #Injectable() which tells Nest that it needs to inject the dependencies defined in the constructor.
You need to import the configModule into your module class for the configService to work. Also, add #Injectable() right above the class name to indicate it is a provider.
This is how you import the module.
//auth.module.ts
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { JwtStrategy } from './jwt.strategy';
#Module({
imports: [ConfigModule],
provider:[JwtStrategy]
})
export class AuthModule {}
NestJs resolves the dependencies between them.
see: https://docs.nestjs.com/techniques/configuration#using-the-configservice
I would also like to point out that since you are not using the configService anywhere but in the super() call the usage of private keyword in front of it is redundant
You can try using this.configService.get('JWT_TOKEN') but it will simply yell at you that you haven't called super
Removing the private keyword will simply avoid making the configService as a class variable and simply treat it as some option passed into it

Get current user in nestjs on a route without an AuthGuard

I use nestjs with passport with jwt strategy. And I want to get a current user on some of my requests.
Currently, I have a decorator that looks like this:
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const CurrentUser = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const user = ctx.switchToHttp().getRequest().user;
if (!user) {
return null;
}
return data ? user[data] : user; // extract a specific property only if specified or get a user object
},
);
It works as intended when i use it on a route with an AuthGuard:
#Get('test')
#UseGuards(AuthGuard())
testRoute(#CurrentUser() user: User) {
console.log('Current User: ', user);
return { user };
}
But how do i make it work (get current user) on non-guarded routes? I need users to be able to post their comments regardless of if they are authorized or not, however, when they are logged in, i need to get their name.
Basically, I need a way to propagate req.user on every(or at least on some of not AuthGuard'ed request), it is really straight forward to do in express by applying passport middleware, but I'm not sure how to do it with #nestjs/passport.
[EDIT]
Thanks to vpdiongzon for pointing me in the right direction, I chose to make a guard based on his answer, that just populates req.user with either user or null:
import { Injectable } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
#Injectable()
export class ApplyUser extends AuthGuard('jwt') {
handleRequest(err: any, user: any) {
if (user) return user;
return null;
}
}
And now I could just use it on any unprotected route that needs to get the current user
#Get('me')
#UseGuards(ApplyUser)
me(#CurrentUser() user: User) {
return { user };
}
You need to apply your AuthGuard to every route regardless but if you have a route that don't require authentication just add a custom decorator, example:
the Auth Guard
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}
handleRequest(err, user, info, context) {
const request = context.switchToHttp().getRequest();
const allowAny = this.reflector.get<string[]>('allow-any', context.getHandler());
if (user) return user;
if (allowAny) return true;
throw new UnauthorizedException();
}
}
Apply globally the AuthGuard in app.module.js
import { APP_GUARD, Reflector } from '#nestjs/core';
import { AppController } from './app.controller';
import { JwtAuthGuard } from './app.guard';
#Module({
imports: ],
controllers: [AppController],
providers: [
{
provide: APP_GUARD,
useFactory: ref => new JwtAuthGuard(ref),
inject: [Reflector],
},
AppService,
],
})
export class AppModule {
}
The custom decorator to allow a route without authentication
import { SetMetadata } from '#nestjs/common';
export const AllowAny = () => SetMetadata('allow-any', true);
Apply AllowAny in a route, if AllowAny decorator is not attached in a controller route it will required a user.
#Post('testPost')
#AllowAny()
async testPost(#Req() request) {
console.log(request.user)
}
"Basically, I need a way to propagate req.user on every(or at least on some of not AuthGuard'ed request), it is realy straight forward to do in express by applying passport middleware, but im not sure how to do it with #nestjs/passport."
To achieve this we write an interceptor because we need to use the UsersService. UserService is part of the dependency injection system. We cannot just import the user service and create a new instance of it ourselves. The service makes use of the users repository and that users repository is setup only through dependency injection.
The thing is we cannot make use of dependency injection with a parameter decorator. This decorator cannot reach into the system in any way and try to get access to some instance of anything inside there. This is how we write the interceptor. I make comments on the code:
// this interceptor will be used by the custom param decoratro to fetch the current User
import {NestInterceptor,ExecutionContext,CallHandler,Injectable} from '#nestjs/common';
import { UsersService } from '../users.service';
#Injectable()
// "implements" guide us how to put together an interceptor
export class CurrentUserInterceptor implements NestInterceptor {
constructor(private userService: UsersService) {}
// handler refers to the route handler
async intercept(context: ExecutionContext, handler: CallHandler) {
const request = context.switchToHttp().getRequest();
const { userId } = request.session || {};
if (userId) {
const user = await this.userService.findOne(userId);
// we need to pass this down to the decorator. SO we assign the user to request because req can be retrieved inside the decorator
// ------THIS IS WHAT YOU WANTED--------
request.currentUser = user;
}
// run the actual route handler
return handler.handle();
}
}
Now you need to register this to the module:
#Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService, AuthService, CurrentUserInterceptor],
})
Inside controller:
#Controller('auth')
#UseInterceptors(CurrentUserInterceptor)
export class UsersController {
constructor("inject services) {}
#Get('/me')
me(#CurrentUser() user: User) {
return user;
}
}
In any route handler you use CurrentUser param decorator, you will have access to "user".
You actually do not need to write a custom param decorator
you could just use the interceptor, its implementation would be different:
#Get('/me')
me(#CurrentUserInterceptor() request: Request) {
// You have access to request.currentUser
return request.currentUser
}
Set interceptor globally
The current setup for the interceptor is tedious. We are applying the interceptor to one controller at a time. (Thats called controlled scope) Instead you could globally make this interceptor available:
users Module:
import { APP_INTERCEPTOR } from '#nestjs/core';
#Module({
// this createes repository
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [
UsersService,
AuthService,
{
provide: APP_INTERCEPTOR,
useClass: CurrentUserInterceptor,
},
],
})
This approach has one downside. Not every controller cares about what the current user is. In those controllers, you still have to make request to the database to fetch the current User.
the parsed userinfo stored in request.user
import {Req} from '#nestjs/common'
import { Request } from 'express'
#Post()
create(#Req() request: Request) {
console.log('user', request.user)
}

NestJS How to add custom Logger to custom ExceptionFilter

I am using NestJS 5.4.0
I have custom LoggerService, it's working perfectly. But, how can I add this LoggerService to ExceptionFilter.
// logger.service.ts
import {Injectable, LoggerService} from '#nestjs/common';
#Injectable()
export class Logger implements LoggerService {
log(message: string) {
console.log(message);
}
error(message: string, trace: string) {
console.error(message);
}
warn(message: string) {
console.warn(message);
}
}
//logger.module.ts
import { Module } from '#nestjs/common';
import {Logger} from '../services/logger.service';
#Module({
providers: [Logger],
exports: [Logger],
})
export class LoggerModule {}
// user.module.ts
import { Module } from '#nestjs/common';
import {UserService} from '../services/user.service';
import {LoggerModule} from './logger.module';
#Module({
imports: [LoggerModule],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
It's working perfectly.
import {Logger} from './logger.service';
export class UserService {
constructor(
private logger: Logger
) {}
private test = () => {
this.logger.log("test"); // log success "test" to console
}
}
But how can I add my custom Logger to ExceptionFilter
// forbidden.exception.filter.ts
import {HttpException, HttpStatus, Injectable} from '#nestjs/common';
#Injectable()
export class ForbiddenException extends HttpException {
constructor(message?: string) {
super(message || 'Forbidden', HttpStatus.FORBIDDEN);
// I want to add my custom logger here!
}
}
Thank for reading.
First of all your class ForbiddenException extends HttpException is not
what it calls ExceptionFilter. ExceptionFilter is
exceptions layer which is responsible for processing all unhandled exceptions across an application
docs
You provided exmaple when you are trying to inject it to your custom HttpException. But thats wrong. Your exception don't have to be responsible for logging. Thats what ExceptionFilter should be responsible for.
Anyway, for now (17 oct 2019) there is no example in official docs how to inject providers to ExceptionFilter.
You can pass it to constructor on init, but you should to get Logger instance before with app.get<T>(...) method.
For example I've changed code from exception-filters docs:
// HttpExceptionFilter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '#nestjs/common';
import { Request, Response } from 'express';
import {MyLogger} from '../MyLogger'
#Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
constructor(private readonly logger: MyLogger) {}
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
if (status >= 500) {
this.logger.error({ request, response });
}
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
and bootstrap.ts code:
// bootstrap.ts
const app = await NestFactory.create(MainModule, {
logger: false,
});
const logger = app.get<MyLogger>(MyLogger);
app.useLogger(logger);
app.useGlobalFilters(new HttpExceptionFilter(logger));
This technique can be used for all this INestApplication methods:
app.useGlobalFilters
app.useGlobalGuards
app.useGlobalInterceptors
app.useGlobalPipes
app.useLogger
app.useWebSocketAdapter
First of all, to use dependency injection with Exception filters you cannot register them using the useGlobalFilters() method:
const app = await NestFactory.create(MainModule, {
logger: false,
});
const logger = app.get<MyLogger>(MyLogger);
app.useLogger(logger);
//Remove this line
//app.useGlobalFilters(new HttpExceptionFilter(logger));
Next in your MainModule, add your custom exception filter as a provider (note: filters are automatically set as global no matter what module you add them to but as a best practice, add them to your top level module):
import { Module } from '#nestjs/common';
import { APP_FILTER } from '#nestjs/core';
import { LoggerModule } from './logger.module';
import { ForbiddenException } from './forbidden.exception.filter.ts';
#Module({
imports: [
LoggerModule //this is your logger module
],
providers: [
{
provide: APP_FILTER, //you have to use this custom provider
useClass: ForbiddenException //this is your custom exception filter
}
]
})
export class MainModule {}
Now you can inject the logger into your custom exception filter:
import {HttpException, HttpStatus, Injectable} from '#nestjs/common';
import { Logger } from './path/to/logger';
#Injectable()
export class ForbiddenException extends HttpException {
constructor(private logger: Logger) {}
catch(exception: HttpException, response) {
this.logger.log('test');
}
}
Pseudo code but I think you get the idea.

Nest.js: Global AuthGuard but with exceptions

I would like to register my AuthenticationGuard, which checks for Authentication, globally on my application, so that by default all routes require authentication.
const authGuard = app
.select(AuthModule)
.get(AuthGuard);
app.useGlobalGuards(authGuard);
What is the best/nest.js way to add route exceptions, so that anonymous routes can also be implemented?
You can actually set metadata for the global AuthGuard so it can determine if it should allow an unauthorized request.
e.g.
Set Global Auth Guard
import { Module } from '#nestjs/common';
import { APP_GUARD } from '#nestjs/core';
import { AuthGuard } from './auth.guard';
#Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}
Use SetMetadata to pass in data to the AuthGuard
import { SetMetadata } from '#nestjs/common';
// Convienience Function
const AllowUnauthorizedRequest = () => SetMetadata('allowUnauthorizedRequest', true);
#Controller()
export class AppController {
#Get('my-unauthorized-path')
#AllowUnauthorizedRequest()
myHandler () {
return { unauthorized: true };
}
}
Use data passed in from SetMetadata to determine if unauthorized request is allowed.
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { validateRequest } from './validateRequest' // your custom implementation
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const allowUnauthorizedRequest = this.reflector.get<boolean>('allowUnauthorizedRequest', context.getHandler());
return allowUnauthorizedRequest || validateRequest(request);
}
}
There is no way to configure such behavior in a succinct way. If useGlobalGuards used, the only way to do this is to customize or extend AuthGuard.
See original issue

Resources