NestJs hangs with Passport Anonymous strategy - nestjs

I have an anonymous passport strategy defined as:
// In anonymous.strategy.ts
import {Strategy} from 'passport-anonymous'
import {PassportStrategy} from '#nestjs/passport'
import {Injectable} from '#nestjs/common'
#Injectable()
export class AnonymousStrategy extends PassportStrategy(Strategy, 'anonymous') {
constructor() {
super()
}
// Request times out unless this function is enabled
// authenticate() {
// return this.success({})
//}
}
Which I then added to the AuthGuards() chain in the controller after 'jwt' one:
#UseGuards(AuthGuard(['jwt', 'anonymous']))
#Post('/posts')
createAd(#Req() req: Request, #Body() createPostDto: CreatePostDto) {
...
}
I can't understand why the request times out unless I enable the authenticate() function in the strategy? After all, I don't have to do this for the passport-jwt strategy. Am I missing something? Thank you.

It's working for me when I added a User object into the success method.
authenticate() {
return this.success(new User());
}

Related

Extends the Request interface to add a fixed user property and extend any other class

I'm doing a server-side application with NestJS and TypeScript in combination with the implementation of Passport JWT.
A little bit of context first:
My JwtStrategy (no issues here):
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private userService: UserService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'hi',
});
}
async validate(payload: IJwtClaims): Promise<UserEntity> {
const { sub: id } = payload;
// Find the user's database record by its "id" and return it.
const user = await this.userService.findById(id);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
According to the documentation about the validate() method:
Passport will build a user object based on the return value of our
validate() method, and attach it as a property on the Request object.
Thanks to this behavior, I can access the user object in my handler like this:
#Get('hi')
example(#Req() request: Request) {
const userId = (request.user as UserEntity).id;
}
Did you notice that I have used a Type Assertion (tells the compiler to consider the user object as UserEntity) ? Without it, I won't have auto-completion about my entity's properties.
As a quick solution, I have created a class that extends the Request interface and include my own property of type UserEntity.
import { Request } from 'express';
import { UserEntity } from 'entities/user.entity';
export class WithUserEntityRequestDto extends Request {
user: UserEntity;
}
Now, my handler will be:
#Get('hi')
example(#Req() request: WithUserEntityRequestDto) {
const userId = request.user.id; // Nicer
}
The real issue now:
I have (and will have more) a handler that will receive a payload, let's call it for this example PasswordResetRequestDto.
export class PasswordResetRequestDto {
currentPassword: string;
newPassword: string;
}
The handler will be:
#Get('password-reset')
resetPassword(#Body() request: PasswordResetRequestDto) {
}
Now, I don't have access to the user's object. I would like to access it to know who is the user that is making this request.
What I have tried:
Use TypeScript Generics and add a new property to my previous WithUserEntityRequestDto class like this:
export class WithUserEntityRequestDto<T> extends Request {
user: UserEntity;
newProp: T;
}
And the handler will be:
#Get('password-reset')
resetPassword(#Req() request: WithUserEntityRequestDto<PasswordResetRequestDto>) {
}
But now the PasswordResetRequestDto will be under newProp, making it not a scalable solution. Any type that I pass as the generic will be under newProp. Also, I cannot extends T because a class cannot extends two classes. I don't see myself doing classes like this all the time.
What I expect to accomplish:
Pass a type to my WithUserEntityRequestDto class to include the passed type properties and also the user object by default. A way that I can do for example:
request: WithUserEntityRequestDto<AwesomeRequestDto>
request: WithUserEntityRequestDto<BankRequestDto>
And the value will be something like:
{
user: UserEntity, // As default, always present
// all the properties of the passed type (T),
// all the properties of the Request interface
}
My goal is to find an easy and scalable way to extends the Request interface and include any type/class on it, while having the user object (UserEntity) always present.
Thanks for the time and any help/advice/approach will be appreciated.
Nestjs provides an elegant solution for your problem, which is Custom decoration
it's common practice to attach properties to the request object. Then you manually extract them in each route handler,
What you have to do is create a user decorator:
//user.decorator.ts
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
then you can simply use it in your controller like this:
#Get('hi')
example(#Req() request: Request,#User() user: UserEntity) {
const userId = user.id;
}

How to use session object in guards with nest-session

In NestJS, using nest-session, I would like to use session object within a guard (CanActivate).
Inside a controller's action this is done by using #Session() but I can't find nor figure out how to fetch this data withing a guard.
First of all create an Interface that contains the session object and extends the express Request object because the session object is a field that nestjs creates and doesn't exists on the Request object.
import { Request } from 'express';
export interface IRequest extends Request {
session: any;
}
Then on your guard, you must import the interface, create a variable with this type then use the execution context to get the request and that's it, the req variable contain the Request object, you can use req.session to get the session object.
import { Injectable, CanActivate, ExecutionContext } from '#nestjs/common';
import { IRequest } from './app.interface'; //Import interface
#Injectable()
export class Guard implements CanActivate {
constructor() {}
canActivate(context: ExecutionContext): boolean {
const req: IRequest = context.switchToHttp().getRequest(); //Request Object
const session = req.session; //Session Object
/*
Do whatever you want with your session here ...
*/
return true;
}
}
You can find more information about the Request object here:
https://docs.nestjs.com/controllers#request-object
https://expressjs.com/en/api.html#req

How to inject the interface of a service in the constructor of a controller in Nestjs?

I have an app that receives a service as a dependency on the controller, so far so good, but I would like to find a way to instead of declaring the specific implementation of that service, to be able to "ask" from the controller for the interface that this service implements to decouple of the concrete implementation of that service. How is this done in nest js?
To do this you have to create an injection token for your interface and use the #Inject() decorator with the injection token when injecting your service. Then in your module you can declare which implementation to provide for that injection token.
Below is a simple greeting service interface and our injection token that will be used when registering our service as a provider.
greeting-service.interface.ts
// This will be our injection token.
export const GREETING_SERVICE = 'GREETING SERVICE';
export interface IGreetingService {
greet(name: string): Promise<string>;
}
A basic service that will implements our greeting interface...
professional-greeting.service.ts
import { Injectable } from '#nestjs/common';
import { IGreetingService } from './greeting-service.interface';
#Injectable()
export class ProfessionalGreetingService implements IGreetingService {
public async greet(name: string): Promise<string> {
return `Hello ${name}, how are you today?`;
}
}
And our greeting module where we register our service using the token...
greeting.module.ts
import { Module } from '#nestjs/common';
import { ProfessionalGreetingService } from './services/professional-greeting.service';
import { GREETING_SERVICE } from './services/greeting-service.interface';
import { GreetingController } from './controllers/greeting.controller';
#Module({
providers: [
{
// You can switch useClass to different implementation
useClass: ProfessionalGreetingService,
provide: GREETING_SERVICE
}
],
controllers: [
GreetingController
]
})
export class GreetingModule {}
Now when we inject our service, we can use the #Inject() decorator with our injection token. Whichever implementation you provived to useClass in our GreetingModule will be injected...
greeting.controller.ts
import { Controller, Get, Inject, Query } from '#nestjs/common';
import { GREETING_SERVICE, IGreetingService } from '../services/greeting-service.interface';
#Controller('greeting')
export class GreetingController {
constructor(
#Inject(GREETING_SERVICE)
private readonly _greetingService: IGreetingService
) {}
#Get()
public async getGreeting(#Query('name') name: string): Promise<string> {
return await this._greetingService.greet(name || 'John');
}
}
https://jasonwhite.xyz/posts/2020/10/20/nestjs-dependency-injection-decoupling-services-with-interfaces/
https://github.com/jmw5598/nestjs-di-decoupling-with-interfaces
https://docs.nestjs.com/fundamentals/custom-providers

How to ignore some routes from #UseGuards() in a controller?

I have a controller like this:
#ApiBearerAuth()
#UseGuards(AuthGuard('jwt'))
#ApiTags('books')
#Controller('books')
export class BooksController {
#Post()
async create(#Body() createBookVm: CreateBookVm) {
//........
}
#Get()
async all() {
//........
}
}
When I access all() rout in above controller without accessToken I get the foloowing error:
{"statusCode":401,"error":"Unauthorized"}
It is a correct behavior but I want ignore all() action from general #UseGuards of the controller. I want access it as a public rout without authorization.
The easiest way is to change Guards to routes:
#ApiBearerAuth()
#ApiTags('books')
#Controller('books')
export class BooksController {
#Post()
#UseGuards(AuthGuard('jwt'))
async create(#Body() createBookVm: CreateBookVm) {
//........
}
#Get()
async all() {
//........
}
}
To provide another answer, albeit one that requires more code, is you could create a custom decorator that assigns metadata to the class and/or the class method. This metadata, in theory, would be for telling the guard to skip the auth check on this entire class, or on this route (depending on how you set the metadata up), and return true so that the request can still flow.
I've got a decorator like this set up here that sets up metadata if you'd like to take a look at how it works.
With this kind of approach, you could bind the guard globally, and then add the #AuthSkip() (or whatever you call it) decorator to the routes or classes you don't want to authorize.
Now you'll need to extend the AuthGuard('jwt') and update the canActivate() method to check for this metadata in the current context. This means that you'll need to add the Reflector as a dependency to the guard class and use it to get the metadata from both the class and the current route (if you went so far as to make it work for ignoring classes and not just routes), and if the metadata exists, then the route was to be skipped, return true from the guard. I make that kind of check here if you'd like to see an example of that in action.
Assuming you have used the app.useGlobalGuards() method inside main.ts file, add the following code inside the auth.guard.ts file:
import { ExecutionContext, Injectable } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { AuthGuard as PassportAuthGaurd } from '#nestjs/passport';
#Injectable()
export class AuthGuard extends PassportAuthGaurd('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.get<boolean>(
'isPublic',
context.getHandler()
);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
I had used pssport jwt method here, but you can alter it according to you, just remember to keep constructor and the logic of canActivate same.
Now in your main.ts modify global guard so we can use Reflectors in it:
const reflector = app.get(Reflector);
app.useGlobalGuards(new AuthGuard(reflector));
Now in order to make routes public we would use a custom decorator, for that create a file named public.decorator.ts and add the following code:
import { SetMetadata } from '#nestjs/common';
export const Public = () => SetMetadata('isPublic', true);
Here we have added a custom metadata value which is same value that we used inside our auth.guard.ts file. Now just add this #Public() decorator on the route that you want to make public:
#Get()
#Public()
async all() {
//........
}
Now your all function won't check for the token authentication.
I found this blog which does the same thing, you can check it out.

Is it possible to override global scoped guard with controller/method scoped one

I'm writing webAPI using NestJS framework. I was not able to override global scoped guard with the one placed on method or controller level. All of my endpoints will use JWT verification guard except one used for logging into the system. Is it possible to create one guard on root level and only override this global guard with #UseGuard() decorator on single method level?
I tried to use guard before listen function call and also use APP_GUARDprovider, but in both cases I'm not able to override this behavior.
Code example:
https://codesandbox.io/embed/nest-yymkf
Just to add my 2 cents.
Instead of defining 2 guards (reject and accept) as the OP have done, I have defined a custom decorator:
import { SetMetadata } from '#nestjs/common'
export const NoAuth = () => SetMetadata('no-auth', true)
The reject guard (AuthGuard) uses Reflector to be able to access the decorator's metadata and decides to activate or not based on it.
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common'
import { Reflector } from '#nestjs/core'
import { Observable } from 'rxjs'
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const noAuth = this.reflector.get<boolean>('no-auth', context.getHandler())
if(noAuth) return true
// else your logic here
}
}
I then bind the reject guard globally in some module:
#Module({
providers: [{
provide: APP_GUARD,
useClass: AuthGuard
}]
})
and proceed to use the decorator where needed:
#NoAuth()
#Get() // anyone can access this
getHello(): string {
return 'Hello Stranger!'
}
#Get('secret') // protected by the global guard
getSecret(): string {
return 'ssshhh!'
}
After a posting the question I figured out the solution for my problem. I should add some custom meta-data into my controller and put a logic inside the guard to read that meta-data.
I have updated the code sample.

Resources